Getting StartedInstall and bootstrap headless foundations 4
FormHeadless input and selection contracts 19
LayoutHeadless structural primitives for expandable and container patterns 6
NavigationHeadless trails, trees, menus, and command surfaces 7
OverlayHeadless modal and floating layer behavior 3
FeedbackHeadless notification and status communication patterns 5
UtilityReusable action, identity, and clipboard behavior 6

Styling contract

The headless drop zone only exposes host-level state attributes. Shape, colors, drag affordances, and rejected-state presentation stay fully owner-controlled.

Host state hooks

HookWhen it appearsUse it for
data-file-uploadAlways present on the host.Base selector for scoping drop-zone styles.
data-draggingWhile a drag gesture is active over the host.Highlight the active drop target.
data-disabledWhenever the directive is disabled.Dim or lock the zone for the passive disabled state.
data-rejectedWhile the drag/drop state is rejected (when surfaced).Optional negative feedback styling.

Host selectors

css
[data-file-upload]
[data-dragging]
[data-disabled]
[data-rejected]

CSS starter

Start with a dashed drop target, then react to data-dragging and data-disabled for hover and locked affordances.

dropzone.css

css
.docs-dropzone {
  border: 2px dashed var(--tng-semantic-border-strong);
  border-radius: 0.85rem;
  padding: 1.5rem;
  text-align: center;
  transition: background 0.15s ease, border-color 0.15s ease;
}

.docs-dropzone[data-dragging] {
  background: color-mix(in srgb, var(--tng-semantic-accent-brand) 10%, transparent);
  border-color: var(--tng-semantic-accent-brand);
}

.docs-dropzone[data-disabled] {
  cursor: not-allowed;
  opacity: 0.55;
}

Accessibility guidance

The primitive does not render its own status region. Mirror filesSelected and filesRejected results into your own aria-live node so the outcome of a drop is announced.

Owner-managed status region

html
<div
  tngFileUpload
  class="docs-dropzone"
  [accept]="'image/*'"
  (filesSelected)="onFilesSelected($event)"
  (filesRejected)="onFilesRejected($event)"
></div>

<p aria-live="polite">{{ statusMessage() }}</p>