1. Wiki
  2. aether-packages
  3. UI Components

UI Components

@aetherAssembly/ui is a small React 19 component library. Components are built on semantic HTML elements, styled entirely with CSS custom properties (--ae-*), and use BEM-style class names scoped to the ae- prefix.

Using the stylesheet

The package ships src/styles.css with default --ae-* token values. Import it once at your app root:

import '@aetherAssembly/ui/src/styles.css'

If you don't import the stylesheet, tokens will be unset and components will be unstyled. You can also skip it entirely and define all tokens yourself.

CSS token reference

All visual styling in every component references these tokens. Override any of them in :root to theme the entire library.

Colors

TokenDefaultUsage
--ae-color-primary#6366f1Primary button background, focus rings, active states
--ae-color-primary-hover#4f46e5Primary hover state
--ae-color-primary-active#4338caPrimary active/pressed state
--ae-color-primary-fg#ffffffText on primary backgrounds
--ae-color-secondary#e5e7ebSecondary button background
--ae-color-secondary-hover#d1d5dbSecondary hover state
--ae-color-secondary-fg#111827Text on secondary backgrounds
--ae-color-danger#ef4444Danger button background, error states
--ae-color-danger-hover#dc2626Danger hover state
--ae-color-danger-fg#ffffffText on danger backgrounds
--ae-color-success#22c55eSuccess badge, positive indicators
--ae-color-warning#f59e0bWarning badge, caution indicators
--ae-color-info#3b82f6Info badge
--ae-color-bg#ffffffComponent background (cards, modals)
--ae-color-surface#f9fafbSubtle surface (input backgrounds)
--ae-color-border#e5e7ebDefault borders
--ae-color-border-focus#6366f1Focus ring color
--ae-color-text#111827Primary text
--ae-color-text-muted#6b7280Secondary/hint text
--ae-color-text-placeholder#9ca3afInput placeholder text

Spacing

TokenValue
--ae-space-10.25rem
--ae-space-20.5rem
--ae-space-30.75rem
--ae-space-41rem
--ae-space-61.5rem
--ae-space-82rem

Typography

TokenDefault
--ae-font-sanssystem-ui, -apple-system, sans-serif
--ae-font-size-sm0.875rem
--ae-font-size-base1rem
--ae-font-size-lg1.125rem
--ae-line-height-tight1.25
--ae-line-height-normal1.5

Radius & shadows

TokenValue
--ae-radius-sm0.25rem
--ae-radius-md0.375rem
--ae-radius-lg0.5rem
--ae-radius-full9999px
--ae-shadow-sm0 1px 2px 0 rgb(0 0 0 / 0.05)
--ae-shadow-md0 4px 6px -1px rgb(0 0 0 / 0.1), 0 2px 4px -2px rgb(0 0 0 / 0.1)
--ae-shadow-lg0 10px 15px -3px rgb(0 0 0 / 0.1), 0 4px 6px -4px rgb(0 0 0 / 0.1)
--ae-transition-fast100ms ease
--ae-transition-base150ms ease

Theming

Override tokens in your app's :root block. You only need to set the tokens you want to change — everything else falls back to the defaults from styles.css. Dark mode works the same way, inside a .dark-mode or @media (prefers-color-scheme: dark) block.

Example — mapping Attyre's warm palette:

:root {
  --ae-color-primary:        var(--gold);
  --ae-color-primary-hover:  var(--gold-dark);
  --ae-color-primary-fg:     #ffffff;
  --ae-color-secondary:      var(--stone-mid);
  --ae-color-secondary-fg:   var(--ink);
  --ae-color-danger:         var(--error);
  --ae-color-bg:             var(--bg-surface);
  --ae-color-surface:        var(--bg-subtle);
  --ae-color-text:           var(--text-primary);
  --ae-color-text-muted:     var(--text-muted);
  --ae-color-border:         var(--border);
  --ae-color-border-focus:   var(--gold);
  --ae-font-sans:            var(--font);
  --ae-radius-md:            var(--radius-md);
}

Component reference

Button

import { Button } from '@aetherAssembly/ui'

<Button variant="primary" size="md" onClick={save}>Save</Button>
<Button variant="danger" loading={isDeleting}>Delete</Button>
<Button variant="ghost" size="sm" disabled>Unavailable</Button>
PropTypeDefaultDescription
variant'primary' | 'secondary' | 'ghost' | 'danger''primary'Visual style
size'sm' | 'md' | 'lg''md'Size
loadingbooleanfalseShows a spinner, sets aria-busy, forces disabled
...restButtonHTMLAttributes—Passed through to <button>

CSS classes: ae-btn, ae-btn--{variant}, ae-btn--{size}, ae-btn__spinner

Card

<Card header={<h2>Title</h2>} footer={<Button size="sm">Action</Button>}>
  <p>Card body content.</p>
</Card>
PropTypeDescription
headerReactNodeOptional header slot, rendered above the body
footerReactNodeOptional footer slot, rendered below the body
childrenReactNodeBody content
classNamestringMerged onto the root element

CSS classes: ae-card, ae-card__header, ae-card__body, ae-card__footer

Badge

<Badge variant="success">Fresh</Badge>
<Badge variant="warning">Expiring soon</Badge>
<Badge variant="danger">Expired</Badge>
PropTypeDefaultDescription
variant'default' | 'success' | 'warning' | 'danger' | 'info''default'Colour scheme
childrenReactNode—Badge label
classNamestring—Merged onto the root element

CSS classes: ae-badge, ae-badge--{variant}

Input

<Input
  label="City"
  hint="e.g. Tokyo Japan"
  error={errors.city}
  value={city}
  onChange={e => setCity(e.target.value)}
/>
PropTypeDescription
labelstringRequired. Rendered as <label> and used to generate the id if one isn't provided.
errorstringIf set, rendered as error text below the input and aria-invalid="true" is applied.
hintstringMuted helper text below the input (below the error if both are set).
...restInputHTMLAttributesPassed through to <input>.

The id is auto-generated from the label ("My Field" → "my-field"). Pass an explicit id prop to override.

CSS classes: ae-input-group, ae-input-group__label, ae-input-group__input, ae-input-group__error, ae-input-group__hint

Modal

<Modal
  open={isOpen}
  onClose={() => setOpen(false)}
  title="Confirm deletion"
>
  <p>This cannot be undone.</p>
  <Button variant="danger" onClick={handleDelete}>Delete</Button>
</Modal>
PropTypeDescription
openbooleanControls visibility. Calls showModal() / close() on the <dialog>.
onClose() => voidCalled when the dialog closes — via close button, Escape, or clicking the backdrop.
titlestringOptional. Renders a header with a close button.
childrenReactNodeBody content.
classNamestringMerged onto the <dialog> element.

Wraps the native <dialog> element. showModal() gives you browser-native focus trapping, Escape to close, and the ::backdrop pseudo-element for the overlay — no JS needed for any of that. Clicking directly on the <dialog> (the backdrop area) calls onClose.

CSS classes: ae-modal, ae-modal__content, ae-modal__header, ae-modal__title, ae-modal__close, ae-modal__body

Last updated: 2026-10-04 AetherAssembly/aether-packages →