Skip to content

Attachment Card

File card showing a type icon, file name, size and a download action.

File card showing a type icon, file name, size and a download action. Compose it from the exported registry parts so local content and behaviour stay in the application while the visual contract stays token-driven.

Root class newt-attachment
import {
  Attachment,
  AttachmentIcon,
  AttachmentMeta,
  AttachmentName,
  AttachmentSize,
} from "@/registry/bases/newt/ui/attachment"
import { Button } from "@/registry/bases/newt/ui/button"

export default function AttachmentDemo() {
  return (
    <Attachment>
      <AttachmentIcon>FILE</AttachmentIcon>
      <AttachmentMeta>
        <AttachmentName>filename.ext</AttachmentName>
        <AttachmentSize>1.2 MB</AttachmentSize>
      </AttachmentMeta>
      <Button variant="secondary" size="icon" aria-label="Download file">
        ⬇
      </Button>
    </Attachment>
  )
}
<script setup lang="ts">
import {
  Attachment,
  AttachmentIcon,
  AttachmentMeta,
  AttachmentName,
  AttachmentSize,
} from "~~/registry/bases/newt/ui/attachment"
import { Button } from "~~/registry/bases/newt/ui/button"
</script>

<template>
  <Attachment>
    <AttachmentIcon>FILE</AttachmentIcon>
    <AttachmentMeta>
      <AttachmentName>filename.ext</AttachmentName>
      <AttachmentSize>1.2 MB</AttachmentSize>
    </AttachmentMeta>
    <Button variant="secondary" size="icon" aria-label="Download file"
      >⬇</Button
    >
  </Attachment>
</template>

Installation

pnpm dlx newtui@latest add attachment
npx newtui@latest add attachment
yarn dlx newtui@latest add attachment
bunx newtui@latest add attachment

Add the components it builds on

Copy the source into your project

import * as React from "react"

import { cn } from "@/lib/utils"

const Attachment = React.forwardRef<
  HTMLDivElement,
  React.HTMLAttributes<HTMLDivElement>
>(({ className, ...props }, ref) => (
  <div
    ref={ref}
    className={cn(
      "flex max-w-[360px] items-center gap-3 rounded-md border border-newt-border bg-newt-bg-surface px-3 py-[10px]",
      className
    )}
    {...props}
  />
))
Attachment.displayName = "Attachment"

const AttachmentIcon = React.forwardRef<
  HTMLDivElement,
  React.HTMLAttributes<HTMLDivElement>
>(({ className, ...props }, ref) => (
  <div
    ref={ref}
    aria-hidden="true"
    className={cn(
      "flex h-9 w-9 shrink-0 items-center justify-center rounded-sm bg-newt-brand font-mono text-xs font-bold text-white",
      className
    )}
    {...props}
  />
))
AttachmentIcon.displayName = "AttachmentIcon"

const AttachmentMeta = React.forwardRef<
  HTMLDivElement,
  React.HTMLAttributes<HTMLDivElement>
>(({ className, ...props }, ref) => (
  <div ref={ref} className={cn("min-w-0 flex-1", className)} {...props} />
))
AttachmentMeta.displayName = "AttachmentMeta"

const AttachmentName = React.forwardRef<
  HTMLDivElement,
  React.HTMLAttributes<HTMLDivElement>
>(({ className, ...props }, ref) => (
  <div
    ref={ref}
    className={cn(
      "truncate text-sm font-medium text-newt-text-link",
      className
    )}
    {...props}
  />
))
AttachmentName.displayName = "AttachmentName"

const AttachmentSize = React.forwardRef<
  HTMLDivElement,
  React.HTMLAttributes<HTMLDivElement>
>(({ className, ...props }, ref) => (
  <div
    ref={ref}
    className={cn("text-xs text-newt-text-muted", className)}
    {...props}
  />
))
AttachmentSize.displayName = "AttachmentSize"

export {
  Attachment,
  AttachmentIcon,
  AttachmentMeta,
  AttachmentName,
  AttachmentSize,
}
<script setup lang="ts">
import type { HTMLAttributes } from "vue"

import { cn } from "@/lib/utils"

const props = defineProps<{ class?: HTMLAttributes["class"] }>()
</script>

<template>
  <div
    :class="
      cn(
        'flex max-w-[360px] items-center gap-3 rounded-md border border-newt-border bg-newt-bg-surface px-3 py-[10px]',
        props.class
      )
    "
  >
    <slot />
  </div>
</template>
<script setup lang="ts">
import type { HTMLAttributes } from "vue"

import { cn } from "@/lib/utils"

const props = defineProps<{ class?: HTMLAttributes["class"] }>()
</script>

<template>
  <div
    aria-hidden="true"
    :class="
      cn(
        'flex h-9 w-9 shrink-0 items-center justify-center rounded-sm bg-newt-brand font-mono text-xs font-bold text-white',
        props.class
      )
    "
  >
    <slot />
  </div>
</template>
<script setup lang="ts">
import type { HTMLAttributes } from "vue"

import { cn } from "@/lib/utils"

const props = defineProps<{ class?: HTMLAttributes["class"] }>()
</script>

<template>
  <div :class="cn('min-w-0 flex-1', props.class)">
    <slot />
  </div>
</template>
<script setup lang="ts">
import type { HTMLAttributes } from "vue"

import { cn } from "@/lib/utils"

const props = defineProps<{ class?: HTMLAttributes["class"] }>()
</script>

<template>
  <div
    :class="cn('truncate text-sm font-medium text-newt-text-link', props.class)"
  >
    <slot />
  </div>
</template>
<script setup lang="ts">
import type { HTMLAttributes } from "vue"

import { cn } from "@/lib/utils"

const props = defineProps<{ class?: HTMLAttributes["class"] }>()
</script>

<template>
  <div :class="cn('text-xs text-newt-text-muted', props.class)">
    <slot />
  </div>
</template>
export { default as Attachment } from "./Attachment.vue"
export { default as AttachmentIcon } from "./AttachmentIcon.vue"
export { default as AttachmentMeta } from "./AttachmentMeta.vue"
export { default as AttachmentName } from "./AttachmentName.vue"
export { default as AttachmentSize } from "./AttachmentSize.vue"

Update the import paths

The files import @/lib/utils and sibling components through the aliases in your components.json. Point them at wherever those live in your project.

Usage

import { Attachment } from "@/components/ui/attachment"

export function Example() {
  return <Attachment />
}
<script setup lang="ts">
import { Attachment, AttachmentIcon, AttachmentMeta, AttachmentName, AttachmentSize } from "@/components/ui/attachment"
</script>

<template>
  <Attachment />
</template>

API reference

Attachment

PropType
children / default slot?React.ReactNode / Vue slot

Content rendered inside the component.

TypeReact.ReactNode / Vue slot
className / class?string

Additional classes merged with the component defaults.

Typestring

The registry exports Attachment, AttachmentIcon, AttachmentMeta, AttachmentName, AttachmentSize. Parts without a table accept the native attributes for the element they render; React uses className, while Vue uses class and fallthrough attributes.

Accessibility

Preserve the native semantics of the rendered elements and pass through ARIA attributes when the surrounding context needs a label or description. Any interactive child needs a visible focus indicator, and meaning must not rely on colour alone.

Tokens

Attachment Card reads its colours, radii and motion from the --newt-* tokens installed with the library. Override a token and every component that reads it follows, including this one — see the token layer. In the plain HTML and CSS distribution the same component is newt-attachment.