@signal9/era-ui 33.0.1 → 34.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,37 @@
1
+ ---
2
+ name: era-ui
3
+ description: Build or review Svelte 5 interfaces that use @signal9/era-ui. Use for component imports, responsive composition, density and surface axes, concentric geometry, material variants, press behavior, motion, typography, accessibility, or choosing Era's public CSS utilities.
4
+ ---
5
+
6
+ # Build with Era UI
7
+
8
+ Use Era's semantic component and class APIs as a coherent system. Do not reproduce component chrome or freeze an axis-responsive value in consumer code.
9
+
10
+ ## Start with the public boundary
11
+
12
+ Import each primitive or composition from its explicit package subpath. The package root is the system layer only.
13
+
14
+ ```svelte
15
+ <script lang="ts">
16
+ import { Button } from '@signal9/era-ui/button';
17
+ import * as Select from '@signal9/era-ui/select';
18
+ import { Conversation } from '@signal9/era-ui/ai/conversation';
19
+ import { cn } from '@signal9/era-ui';
20
+ </script>
21
+ ```
22
+
23
+ Import `@signal9/era-ui/css` once in the application. Prefer named utilities such as `tier-control`, `gap-gutter`, `p-card`, `material-popover`, and `era-text-trim`. Use a `--era-*` custom property only when no named utility expresses the requirement.
24
+
25
+ ## Preserve the system axes
26
+
27
+ Era has five orthogonal axes: density (`data-mode`), surface (`data-surface`), corners (`data-corners`), font (`data-font`), and motion (`data-motion`). Root values establish application defaults; supported portalling components relay the nearest subtree values to their content. Subtree overrides are therefore valid for both ordinary and portalled descendants.
28
+
29
+ Use `ModeProvider` when state should drive axes. Use data attributes directly for static regions. Never branch component markup on a material such as `glass` or `bevel`; semantic recipes resolve the material.
30
+
31
+ ## Load only the relevant reference
32
+
33
+ - [Geometry](references/geometry.md): tiers, even gaps, concentricity, and padding.
34
+ - [Materials and motion](references/materials.md): surface recipes, press travel, state semantics, and motion.
35
+ - [Composition and review](references/composition.md): component topology, consumer classes, text, and the final review checklist.
36
+
37
+ The installed package exposes its machine-readable utility reference at `@signal9/era-ui/utilities.json`; the documentation site renders the same generated data for people.
@@ -0,0 +1,19 @@
1
+ # Composition and review
2
+
3
+ Primitive subpaths export either a named component or a namespace of parts. Follow the installed type surface instead of importing source files or relying on an aggregate barrel. AI compositions live under `@signal9/era-ui/ai/<part>`; OS compositions live at `@signal9/era-ui/os`.
4
+
5
+ Consumer `class` values merge last, so use them for layout and intentional semantic overrides. Do not copy a component's internal variant recipe into application code.
6
+
7
+ For control text beside an icon, use `era-text-trim`; use `era-text-trim-caps` for all-caps labels. A single glyph should retain the matching square minimum. Use `text-body`, `text-fg`, `text-bright`, and `text-muted` for hierarchy that remains axis-aware.
8
+
9
+ ## Review checklist
10
+
11
+ - Every import is a documented explicit package subpath.
12
+ - Geometry uses a tier or named optical surface spacing rather than raw dimensions.
13
+ - Sibling gaps and edge padding agree.
14
+ - Nested bounded corners use the tier relationship rather than independent radii.
15
+ - Surface styling uses semantic material/state recipes.
16
+ - Transient, latched, disabled, busy, and audience meanings do not share a visual channel.
17
+ - Pressed content moves with its face without moving surrounding layout.
18
+ - Motion respects the motion axis and reduced-motion preferences.
19
+ - Interactive markup remains valid, keyboard reachable, and labelled.
@@ -0,0 +1,24 @@
1
+ # Geometry
2
+
3
+ Every bounded size derives from the density axis. Use role names rather than guessed dimensions:
4
+
5
+ | Tier | Typical role |
6
+ | --------- | ---------------------------------- |
7
+ | `icon` | glyphs and switch thumbs |
8
+ | `pill` | badges nested inside controls |
9
+ | `chip` | standalone chips and switch tracks |
10
+ | `control` | buttons, inputs, and triggers |
11
+ | `bar` | bounded headers and rails |
12
+
13
+ Use `tier-icon`, `tier-pill`, `tier-chip`, `tier-control`, or `tier-bar` when height and radius belong together. Width and minimum helpers use the same role names, such as `size-icon` and `min-w-control`.
14
+
15
+ ## Two invariants
16
+
17
+ 1. Edge padding equals the gap between siblings. `p-gutter` and `gap-gutter` are the common pair.
18
+ 2. For nested bounded tiers, `outer radius − inner radius = inset`. The tier ladder and named pair insets encode this identity; do not tune radii independently.
19
+
20
+ An unbounded card or panel has no height tier. Use optical padding (`p-panel`, `p-card`, or `p-content`) and its semantic surface radius. Bounded controls use geometric insets and tier geometry.
21
+
22
+ Use `rounded-*` only when a corner must be expressed independently. Stock fixed radii such as `rounded-md` do not respond to Era's corner axis.
23
+
24
+ For a genuinely custom derived measurement with no named utility, calculate from `--era-sp` so density still propagates. Treat that token path as an escape hatch, not the default spelling for named classes.
@@ -0,0 +1,15 @@
1
+ # Materials and motion
2
+
3
+ Choose semantic material recipes, not surface-specific class branches:
4
+
5
+ - `material-popover` for transient floating chrome.
6
+ - `material-panel` for persistent floating chrome.
7
+ - `shadow` for a resting raised face, `shadow-lg` for floating chrome, `shadow-well` for recessed content, and `shadow-pressed` for a latched or pressed face.
8
+ - `bg-highlight` with `shadow-highlight` for transient hover or focus feedback.
9
+ - `bg-hover` for held, selected, or current state.
10
+
11
+ The `pressable` recipe owns physical travel through `--era-press-sink-x` and `--era-press-sink-y`. Mark the direct content plane as the component implementation expects so bevel content travels with its face; do not translate the outer layout box or add consumer-side active offsets.
12
+
13
+ Use the motion-aware `transition-*`, `duration-base`, and `ease-base` utilities. They respond to `data-motion="instant"` and reduced-motion preferences. Do not hardcode a duration or easing curve for ordinary interaction feedback.
14
+
15
+ Opacity and flattening already communicate disabled or busy state. Do not reuse those channels to communicate a different concept such as audience or visibility.
@@ -1,67 +0,0 @@
1
- export * as Accordion from './accordion/index.js';
2
- export * as AlertDialog from './alert-dialog/index.js';
3
- export { AspectRatio } from './aspect-ratio/index.js';
4
- export * as Avatar from './avatar/index.js';
5
- export { Badge } from './badge/index.js';
6
- export * as Calendar from './calendar/index.js';
7
- export { Chip } from './chip/index.js';
8
- export { CodeBlock } from './code-block/index.js';
9
- export { CopyButton } from './copy-button/index.js';
10
- export { Cycle, type CycleOption } from './cycle/index.js';
11
- export * as Checkbox from './checkbox/index.js';
12
- export * as Combobox from './combobox/index.js';
13
- export * as Collapsible from './collapsible/index.js';
14
- export * as Command from './command/index.js';
15
- export * from './command-bar/index.js';
16
- export * as Step from './step/index.js';
17
- export * as ContextMenu from './context-menu/index.js';
18
- export * as DateField from './date-field/index.js';
19
- export * as DatePicker from './date-picker/index.js';
20
- export * as DateRangeField from './date-range-field/index.js';
21
- export * as DateRangePicker from './date-range-picker/index.js';
22
- export * as Dialog from './dialog/index.js';
23
- export * as DropdownMenu from './dropdown-menu/index.js';
24
- export { FileUpload } from './file-upload/index.js';
25
- export * as Menubar from './menubar/index.js';
26
- export * as Nav from './nav/index.js';
27
- export * as Menu from './menu/index.js';
28
- export { Meter } from './meter/index.js';
29
- export { Progress } from './progress/index.js';
30
- export * as RadioGroup from './radio-group/index.js';
31
- export * as RangeCalendar from './range-calendar/index.js';
32
- export * as RatingGroup from './rating-group/index.js';
33
- export * as ScrollArea from './scroll-area/index.js';
34
- export * as Select from './select/index.js';
35
- export * as Sheet from './sheet/index.js';
36
- export * as Slider from './slider/index.js';
37
- export * as Table from './table/index.js';
38
- export * as Tabs from './tabs/index.js';
39
- export * as TimeField from './time-field/index.js';
40
- export * as TimeRangeField from './time-range-field/index.js';
41
- export * as Tooltip from './tooltip/index.js';
42
- export * as ToggleGroup from './toggle-group/index.js';
43
- export * as Timeline from './timeline/index.js';
44
- export * as Tree from './tree/index.js';
45
- export * as Toolbar from './toolbar/index.js';
46
- export { Bar } from './bar/index.js';
47
- export { Button } from './button/index.js';
48
- export { ButtonGroup } from './button-group/index.js';
49
- export { Card } from './card/index.js';
50
- export { Input, Textarea } from './input/index.js';
51
- export { KV } from './kv/index.js';
52
- export { Label } from './label/index.js';
53
- export * as LinkPreview from './link-preview/index.js';
54
- export { Logo } from './logo/index.js';
55
- export type { LogoPalette, LogoState, LogoParams } from './logo/index.js';
56
- export { Mode } from './mode/index.js';
57
- export { Pane, Root as PaneRoot, Handle as PaneHandle, Content as PaneContent, Close as PaneClose, Tabs as PaneTabs, TabList as PaneTabList, Tab as PaneTab, TabPanel as PaneTabPanel } from './pane/index.js';
58
- export * as Pagination from './pagination/index.js';
59
- export * as NavigationMenu from './navigation-menu/index.js';
60
- export * as Popover from './popover/index.js';
61
- export * as PinInput from './pin-input/index.js';
62
- export { modes, defaultMode, type Mode as DensityMode, surfaces, defaultSurface, surfaceRegistry, type Surface, type SurfaceMeta, corners, defaultCorners, type Corners, fonts, defaultFont, type Font, motions, defaultMotion, type Motion, axisRegistry, axisKeys, type AxisKey, type AxisMeta, type AxisLevel } from './provider/index.js';
63
- export { Separator } from './separator/index.js';
64
- export { Skeleton } from './skeleton/index.js';
65
- export { Switch } from './switch/index.js';
66
- export { Toggle } from './toggle/index.js';
67
- export { VideoPlayer } from './video-player/index.js';
package/dist/ui/index.js DELETED
@@ -1,70 +0,0 @@
1
- export * as Accordion from './accordion/index.js';
2
- export * as AlertDialog from './alert-dialog/index.js';
3
- export { AspectRatio } from './aspect-ratio/index.js';
4
- export * as Avatar from './avatar/index.js';
5
- export { Badge } from './badge/index.js';
6
- export * as Calendar from './calendar/index.js';
7
- export { Chip } from './chip/index.js';
8
- export { CodeBlock } from './code-block/index.js';
9
- export { CopyButton } from './copy-button/index.js';
10
- export { Cycle } from './cycle/index.js';
11
- export * as Checkbox from './checkbox/index.js';
12
- export * as Combobox from './combobox/index.js';
13
- export * as Collapsible from './collapsible/index.js';
14
- export * as Command from './command/index.js';
15
- export * from './command-bar/index.js';
16
- export * as Step from './step/index.js';
17
- export * as ContextMenu from './context-menu/index.js';
18
- export * as DateField from './date-field/index.js';
19
- export * as DatePicker from './date-picker/index.js';
20
- export * as DateRangeField from './date-range-field/index.js';
21
- export * as DateRangePicker from './date-range-picker/index.js';
22
- export * as Dialog from './dialog/index.js';
23
- export * as DropdownMenu from './dropdown-menu/index.js';
24
- export { FileUpload } from './file-upload/index.js';
25
- export * as Menubar from './menubar/index.js';
26
- export * as Nav from './nav/index.js';
27
- export * as Menu from './menu/index.js';
28
- export { Meter } from './meter/index.js';
29
- export { Progress } from './progress/index.js';
30
- export * as RadioGroup from './radio-group/index.js';
31
- export * as RangeCalendar from './range-calendar/index.js';
32
- export * as RatingGroup from './rating-group/index.js';
33
- export * as ScrollArea from './scroll-area/index.js';
34
- export * as Select from './select/index.js';
35
- export * as Sheet from './sheet/index.js';
36
- export * as Slider from './slider/index.js';
37
- export * as Table from './table/index.js';
38
- export * as Tabs from './tabs/index.js';
39
- export * as TimeField from './time-field/index.js';
40
- export * as TimeRangeField from './time-range-field/index.js';
41
- export * as Tooltip from './tooltip/index.js';
42
- export * as ToggleGroup from './toggle-group/index.js';
43
- export * as Timeline from './timeline/index.js';
44
- export * as Tree from './tree/index.js';
45
- export * as Toolbar from './toolbar/index.js';
46
- export { Bar } from './bar/index.js';
47
- export { Button } from './button/index.js';
48
- export { ButtonGroup } from './button-group/index.js';
49
- export { Card } from './card/index.js';
50
- export { Input, Textarea } from './input/index.js';
51
- export { KV } from './kv/index.js';
52
- export { Label } from './label/index.js';
53
- export * as LinkPreview from './link-preview/index.js';
54
- export { Logo } from './logo/index.js';
55
- export { Mode } from './mode/index.js';
56
- export { Pane, Root as PaneRoot, Handle as PaneHandle, Content as PaneContent, Close as PaneClose, Tabs as PaneTabs, TabList as PaneTabList, Tab as PaneTab, TabPanel as PaneTabPanel } from './pane/index.js';
57
- export * as Pagination from './pagination/index.js';
58
- export * as NavigationMenu from './navigation-menu/index.js';
59
- export * as Popover from './popover/index.js';
60
- export * as PinInput from './pin-input/index.js';
61
- // `Mode` as a top-level identifier is reserved for the component (above), so
62
- // the density-union type can't be re-exported here under its own name — it is
63
- // available (under its original name) from the `@signal9/era-ui/provider`
64
- // subpath, or aliased as `DensityMode` below. Everything else flows through.
65
- export { modes, defaultMode, surfaces, defaultSurface, surfaceRegistry, corners, defaultCorners, fonts, defaultFont, motions, defaultMotion, axisRegistry, axisKeys } from './provider/index.js';
66
- export { Separator } from './separator/index.js';
67
- export { Skeleton } from './skeleton/index.js';
68
- export { Switch } from './switch/index.js';
69
- export { Toggle } from './toggle/index.js';
70
- export { VideoPlayer } from './video-player/index.js';
package/skill/SKILL.md DELETED
@@ -1,225 +0,0 @@
1
- ---
2
- name: era-ui
3
- description: |
4
- Design-system methodology for building UIs with @signal9/era-ui (Svelte 5 +
5
- Tailwind v4). Use when: writing or reviewing any component, layout, or page
6
- in a project that uses era-ui — sizing controls, spacing/padding, radii,
7
- surfaces, motion, or composing library components. Covers: the named class
8
- API, the sp-derived tier ladder, the even-gap and concentricity laws,
9
- geometric vs optical padding, surface/shadow rules, motion, and the
10
- mistakes agents make.
11
- version: 1.1.0
12
- ---
13
-
14
- # Building with era-ui
15
-
16
- Every size, gap, and radius derives from ONE number per density mode: `sp`
17
- (8/10/12/16 for dense/balanced/spacious/touch). Never write a hardcoded px,
18
- rem, radius, duration, or color — there is a class for everything, and the
19
- whole system re-derives per element, so any `data-*` axis override on any
20
- subtree just works.
21
-
22
- **Write named classes, not var classes.** Era's public styling API is pure
23
- Tailwind-looking utilities: `tier-control px-gutter shadow`, never
24
- `h-(--era-h-control)`-style var spellings. Where Tailwind already names the concept
25
- era overloads it (bare `shadow` is the
26
- surface chrome, `transition-colors` rides the motion axis); where it doesn't,
27
- era registers its own (`tier-control`, `gap-gutter`, `p-card`). The `--era-*` custom
28
- properties remain public as the escape hatch for the few things without a
29
- name — noted inline below — but reaching for one when a named class exists is
30
- a review finding.
31
-
32
- ## The five axes (data attributes, all orthogonal)
33
-
34
- | Axis | Attribute | Values |
35
- | ------- | -------------- | ------------------------------------- |
36
- | Density | `data-mode` | `dense` `balanced` `spacious` `touch` |
37
- | Surface | `data-surface` | `flat` `glass` `bevel` |
38
- | Corners | `data-corners` | `rounded` `square` |
39
- | Font | `data-font` | `mono` (default) `sans` `serif` |
40
- | Motion | `data-motion` | `instant` `normal` `extra` |
41
-
42
- **SET EVERY AXIS ON `<html>`.** A subtree override works for everything
43
- rendered inside that subtree — and does NOT reach anything that portals.
44
- Dialog, AlertDialog, Sheet, DropdownMenu, ContextMenu, Menubar, Tooltip and
45
- LinkPreview render at `document.body`, so they take the ROOT's axes, not the
46
- ones on the wrapper their trigger sits in.
47
-
48
- Measured: a `<Card>` inside a `data-surface="bevel"` div resolves to bevel and
49
- gets the chisel; a `<Dialog.Content>` opened from that same div resolves to
50
- `flat` and gets flat's chrome. Two surfaces in one screen, and nothing errors.
51
-
52
- So a subtree override is for a REGION you want to look different — a settings
53
- panel that is deliberately denser. If you want the whole app on bevel, set it
54
- at the root and never on a wrapper. The same applies to `data-mode`: a
55
- `data-mode="spacious"` section will open balanced menus if the root is
56
- balanced.
57
-
58
- A monospace root needs `html { font-size: 16px }` (browser quirk: monospace
59
- roots default to ~13px, silently shrinking every rem-derived token).
60
-
61
- ## The tier ladder
62
-
63
- Named for the ROLE each rung plays, never for its size — a scale whose names
64
- can invert is a scale nobody reads correctly (this library once shipped an
65
- `xxs` that was bigger than its `xs`).
66
-
67
- | Tier | Geometry | Width/min-width helpers | Use for |
68
- | --------- | -------------- | ---------------------------- | ------------------------------------ |
69
- | `icon` | `tier-icon` | `w-icon`, `min-w-icon` | a lucide glyph, switch thumbs |
70
- | `pill` | `tier-pill` | `w-pill`, `min-w-pill` | a badge/chip nested INSIDE a control |
71
- | `chip` | `tier-chip` | `w-chip`, `min-w-chip` | standalone pills, switch tracks, KV |
72
- | `control` | `tier-control` | `w-control`, `min-w-control` | buttons, inputs, selects, triggers |
73
- | `bar` | `tier-bar` | `w-bar`, `min-w-bar` | bars and bounded headers |
74
-
75
- `min-h-*`, `min-w-*` and `w-*` exist across the same rungs. There is no escape
76
- hatch here any more: `min-w-control` is a real class, because role names collide
77
- with nothing in Tailwind's container scale the way `min-w-md` did.
78
-
79
- `rounded-sm/md/lg` are TAILWIND's, not era's — era stopped shadowing them when
80
- the ladder was renamed. Reaching for one gets a fixed radius that ignores the
81
- corners axis.
82
-
83
- ## The two laws
84
-
85
- 1. **Even gaps** — the padding between a container's edge and its children
86
- must equal the gap between the children, on every side. `gap-gutter`
87
- (= sp/2) is the universal inter-element gap, and `p-gutter` (+ `px-`/`py-`)
88
- is the matching gutter a scrollable list or rail owes its rows.
89
- 2. **Concentricity** — `rd-T − rd-U = (h-T − h-U)/2` for every tier pair.
90
- `tier-T` binds height and radius so they cannot drift. A bounded container
91
- names the ACTUAL inner tier and owns the matching inset; nested corners then
92
- share a center automatically. The concentric insets are advanced enough to stay
93
- token-form, and each is named for the pair it centres:
94
- `--era-icon-inset-chip` (an icon inside a chip, = sp/2),
95
- `--era-icon-inset-pill`, `--era-pill-inset-control`, `--era-pill-inset-chip`,
96
- `--era-icon-inset-control`, `--era-icon-inset-bar`, `--era-chip-inset-control`,
97
- `--era-chip-inset-bar`.
98
-
99
- **The boundary**: an unbounded surface has no height tier. Its exposed corner is
100
- either optical or derives OUTWARD from a declared corner-bearing child. Never
101
- derive a bounded row's radius inward from an ancestor; that makes the row's
102
- relationship with its own badge or chip depend on unrelated outer chrome.
103
-
104
- ## Padding: geometric vs optical
105
-
106
- - **Bounded (tier-height) containers** — bars, triggers, pills: geometric.
107
- Side padding `px-gutter` (sp/2 — one token, not a per-tier family);
108
- text-symmetric vertical centering `p`/`px`/`py-inset-{pill,chip,control}` =
109
- (h-T − text)/2; pill side breathing `px-pill`/`px-chip` = (h-T − 0.6·text)/2
110
- (text wants ~2× more horizontal than vertical padding); text-field side
111
- padding `px-field`.
112
- - **Unbounded containers** — optical: `p-panel` (compact shells), `p-card`
113
- (cards, dialogs, popovers), `p-content` (full content panels). Super-linear
114
- across modes so whitespace keeps pace with growing controls while text
115
- stays 14px. Never pad a card with `px-*` or ad-hoc `calc`.
116
-
117
- ## Surfaces and shadows
118
-
119
- - Depth is **shadow, not border**: raised chrome takes bare `shadow`;
120
- floating panels take `shadow-lg`; recessed content containers (inputs,
121
- tracks, wells) take `shadow-well`; pressed/latched faces take
122
- `shadow-pressed`. On press, bevel sinks the CONTENT via
123
- `--era-press-sink-x`/`-y` while the box holds still.
124
- - **Transient vs latched fills**: hover/focus/highlighted feedback uses
125
- `bg-highlight` + `shadow-highlight` (surface-owned; translucent on glass).
126
- Held state (toggle on, current tab, active button) uses opaque `bg-hover`.
127
- Never swap.
128
- - Panels: use `material-popover` for transient floating chrome and
129
- `material-panel` for persistent floating chrome. These atomically bind fill,
130
- relief, and backdrop without naming flat/glass/bevel. Use `surface-backdrop`
131
- only when a custom surface must choose its own fill and relief. Modal scrims
132
- use `bg-overlay`. Menu panels pad with `p-menu` and their rows take
133
- `rounded-menu-item` so the row highlight stays concentric inside the frame.
134
-
135
- ## Motion
136
-
137
- The bare `transition-*` utilities default to the motion axis — `transition-colors`
138
- alone is on-design: 0s at `instant` (also forced by reduced-motion), longer at
139
- `extra` with a gentle overshoot. Name the axis explicitly with `ease-base` /
140
- `duration-base` when overriding; never `duration-150` or a literal bezier.
141
- Deriving compound timings (shimmer loops, cursor pulses) as multiples of
142
- `--era-duration` makes them motion-axis-aware for free.
143
-
144
- ## Text
145
-
146
- - `text-body` for control/body text (density-aware size); `text-fg` default,
147
- `text-bright` emphasis, `text-muted` secondary.
148
- - Single-glyph elements carry a min-width equal to their tier height (a lone
149
- character renders square, never a skinny pill) — `min-w-control`, see
150
- the ladder note.
151
- - Single-line control text next to icons carries `era-text-trim`
152
- (`line-height: 1` + `text-box: trim-both ex alphabetic`) — flexbox centers
153
- the line box, not the ink; the utility centers the x-height band and holds
154
- alignment to <0.5px across fonts. `era-text-trim-caps` for ALL-CAPS strings.
155
- Never a translateY nudge, never `cap alphabetic`/`cap text` for mixed case.
156
- - Fonts: the library's default mono stack (ui-monospace → SF Mono → Menlo →
157
- Consolas → Liberation Mono) is the reference for all text-metric guarantees
158
- (ink centering, pill paddings, square floors). Overriding `--font-mono` is
159
- supported, but re-check control-text alignment with your font — metrics
160
- move with the face.
161
- - `font-mono` is a display-token convention (Badge, Chip, KV, code), not a
162
- universal rule.
163
- - Headings balance and prose wraps pretty automatically — don't re-add
164
- `text-balance`/`text-pretty` except on non-semantic prose elements.
165
-
166
- ## CSS utilities (classes, not components)
167
-
168
- Not everything era exposes is a component — these ride along with the stylesheet
169
- and need no import. Reach for one before hand-rolling the same styling or bending
170
- a component into the role. Full reference (including every named scale above):
171
- `/utilities.md`; machine-readable list: `/utilities.json` (or
172
- `@signal9/era-ui/utilities.json`).
173
-
174
- | Class | Use it for |
175
- | -------------------- | ---------------------------------------------------------------------------------------------------------------------- |
176
- | `era-link` | inline text link in body copy/prose — NOT `Button variant="link"` (that's a control) and never a hand-rolled underline |
177
- | `era-interactive` | the focus/disabled recipe on a custom control (every era wrapper carries it) |
178
- | `era-text-trim` | ink-centering single-line control text beside an icon; fixed-height elements only |
179
- | `era-text-trim-caps` | the same for ALL-CAPS strings (kbd hints, acronym badges) |
180
- | `era-shimmer` | a "working…" / streaming label — motion-aware, dies at `instant` and under reduced-motion |
181
- | `scrollbar-none` | an overflowing strip that scrolls with no visible bar (ScrollArea is for when the bar IS the design) |
182
- | `material-popover` | complete transient floating material: raised fill, floating relief, and surface-owned backdrop |
183
- | `material-panel` | complete persistent floating material: resting fill, floating relief, and surface-owned backdrop |
184
- | `surface-backdrop` | backdrop treatment only, for a custom surface whose fill and relief are deliberately separate |
185
- | `pressable` | physical pressed face; moves its marked direct content plane by the surface-owned travel |
186
-
187
- ## Composing the library
188
-
189
- One entry point per component — nothing re-exports them together, so an import
190
- can never pull in a component you did not ask for. The ROOT is the system layer
191
- (`cn`, `tv`, the axis registry, `ModeProvider`) and holds no components.
192
-
193
- ```svelte
194
- <script lang="ts">
195
- import { Button } from '@signal9/era-ui/button';
196
- import { Badge } from '@signal9/era-ui/badge';
197
- import * as Select from '@signal9/era-ui/select'; // namespaced parts: Select.Root…
198
- import { Desktop } from '@signal9/era-ui/os';
199
- import { Conversation } from '@signal9/era-ui/ai/conversation';
200
-
201
- import { cn } from '@signal9/era-ui'; // the root: system only
202
- </script>
203
- ```
204
-
205
- - `ui` primitives are minimal bits-ui wrappers — all bits props/snippets pass
206
- through; style via `class` (merged with tailwind-merge, consumer wins — the
207
- named classes conflict-resolve against internals, so `class="h-bar"` on a
208
- control-tier control just works).
209
- - `@signal9/era-ui/os`: WindowManager + workspaces, Desktop, Pane, Taskbar
210
- (built-in command bar), Toaster. `@signal9/era-ui/ai/<part>`: Conversation,
211
- Message, Response, PromptInput, Tool/Reasoning/Task over the Step primitive.
212
- - List-item keyboard highlight is `data-[highlighted]:bg-hover` — never
213
- `hover:text-bright` on items.
214
-
215
- ## Review checklist (the mistakes to catch)
216
-
217
- 1. Hardcoded px/rem/radius/duration/color → replace with the named class.
218
- 2. Radius chosen by bulk instead of corner region.
219
- 3. Unequal edge padding vs inner gap at any nesting level.
220
- 4. `px-*`/geometric padding on an unbounded container (or `p-card` on a pill).
221
- 5. `bg-hover` used for hover feedback, or `bg-highlight` for latched state.
222
- 6. Icon not `size-icon`; single glyph without the `min-w-*` square floor.
223
- 7. A `-(--era-*)` var class where a named class exists (`h-(--era-h-control)` →
224
- `h-control`); if the var form is genuinely needed, always the paren shorthand —
225
- never `x-[var(--era-y)]`.