@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.
- package/CHANGELOG.md +2 -2
- package/dist/era-ui.css +1 -1
- package/dist/index.d.ts +3 -3
- package/dist/index.js +3 -3
- package/dist/styles/surfaces/bevel.css +27 -51
- package/dist/ui/provider/index.d.ts +3 -3
- package/dist/ui/provider/index.js +3 -3
- package/package.json +474 -10
- package/skills/era-ui/SKILL.md +37 -0
- package/skills/era-ui/references/composition.md +19 -0
- package/skills/era-ui/references/geometry.md +24 -0
- package/skills/era-ui/references/materials.md +15 -0
- package/dist/ui/index.d.ts +0 -67
- package/dist/ui/index.js +0 -70
- package/skill/SKILL.md +0 -225
|
@@ -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.
|
package/dist/ui/index.d.ts
DELETED
|
@@ -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)]`.
|