@starci/grammar 0.11.0 → 0.12.0
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/README.md +127 -7
- package/dist/.build-stamp.json +4 -4
- package/dist/common/components-avatar-group.css +65 -0
- package/dist/common/components-forms.css +58 -73
- package/dist/common/components-list-panel.css +13 -0
- package/dist/common/components-navigation.css +31 -32
- package/dist/common/components-overlays.css +26 -1
- package/dist/common/components-surface-controls.css +15 -0
- package/dist/common/registry.d.ts +19 -18
- package/dist/common/registry.d.ts.map +1 -1
- package/dist/common/registry.js +2 -2
- package/dist/common/registry.js.map +1 -1
- package/dist/common/renderers-forms.d.ts +12 -12
- package/dist/common/renderers-navigation.d.ts +5 -5
- package/dist/common/renderers-navigation.d.ts.map +1 -1
- package/dist/common/renderers-navigation.js.map +1 -1
- package/dist/common/renderers.d.ts +2 -1
- package/dist/common/renderers.d.ts.map +1 -1
- package/dist/common/renderers.js +1 -0
- package/dist/common/renderers.js.map +1 -1
- package/dist/common/rule-catalog.generated.d.ts +1 -1
- package/dist/common/rule-catalog.generated.d.ts.map +1 -1
- package/dist/common/rule-catalog.generated.js +3 -2
- package/dist/common/rule-catalog.generated.js.map +1 -1
- package/dist/common/styles.css +61 -2
- package/dist/core/OtpInput.d.ts +3 -0
- package/dist/core/OtpInput.d.ts.map +1 -1
- package/dist/core/OtpInput.js +4 -2
- package/dist/core/OtpInput.js.map +1 -1
- package/dist/core/branch/AlertDialog/index.d.ts.map +1 -1
- package/dist/core/branch/AlertDialog/index.js +25 -20
- package/dist/core/branch/AlertDialog/index.js.map +1 -1
- package/dist/core/branch/ComboBox/index.d.ts +2 -2
- package/dist/core/branch/ComboBox/index.d.ts.map +1 -1
- package/dist/core/branch/ComboBox/index.js +3 -1
- package/dist/core/branch/ComboBox/index.js.map +1 -1
- package/dist/core/branch/DatePicker/index.d.ts +2 -2
- package/dist/core/branch/DatePicker/index.d.ts.map +1 -1
- package/dist/core/branch/DatePicker/index.js +3 -1
- package/dist/core/branch/DatePicker/index.js.map +1 -1
- package/dist/core/branch/DateRangePicker/index.d.ts +2 -2
- package/dist/core/branch/DateRangePicker/index.d.ts.map +1 -1
- package/dist/core/branch/DateRangePicker/index.js +3 -1
- package/dist/core/branch/DateRangePicker/index.js.map +1 -1
- package/dist/core/branch/Dialog/index.d.ts.map +1 -1
- package/dist/core/branch/Dialog/index.js +6 -2
- package/dist/core/branch/Dialog/index.js.map +1 -1
- package/dist/core/branch/Drawer/index.d.ts.map +1 -1
- package/dist/core/branch/Drawer/index.js +6 -2
- package/dist/core/branch/Drawer/index.js.map +1 -1
- package/dist/core/branch/DropdownMenu/index.d.ts +16 -4
- package/dist/core/branch/DropdownMenu/index.d.ts.map +1 -1
- package/dist/core/branch/DropdownMenu/index.js +14 -5
- package/dist/core/branch/DropdownMenu/index.js.map +1 -1
- package/dist/core/branch/ListBox/index.d.ts +20 -5
- package/dist/core/branch/ListBox/index.d.ts.map +1 -1
- package/dist/core/branch/ListBox/index.js +19 -9
- package/dist/core/branch/ListBox/index.js.map +1 -1
- package/dist/core/branch/Popover/index.d.ts.map +1 -1
- package/dist/core/branch/Popover/index.js +2 -1
- package/dist/core/branch/Popover/index.js.map +1 -1
- package/dist/core/branch/Rail/index.d.ts.map +1 -1
- package/dist/core/branch/Rail/index.js +4 -2
- package/dist/core/branch/Rail/index.js.map +1 -1
- package/dist/core/branch/Select/index.d.ts +6 -3
- package/dist/core/branch/Select/index.d.ts.map +1 -1
- package/dist/core/branch/Select/index.js +14 -21
- package/dist/core/branch/Select/index.js.map +1 -1
- package/dist/core/branch/SurfaceAccordionCard/index.d.ts.map +1 -1
- package/dist/core/branch/SurfaceAccordionCard/index.js +3 -1
- package/dist/core/branch/SurfaceAccordionCard/index.js.map +1 -1
- package/dist/core/branch/SurfaceCard/index.d.ts.map +1 -1
- package/dist/core/branch/SurfaceCard/index.js +3 -1
- package/dist/core/branch/SurfaceCard/index.js.map +1 -1
- package/dist/core/branch/SurfaceListCard/index.d.ts.map +1 -1
- package/dist/core/branch/SurfaceListCard/index.js +3 -1
- package/dist/core/branch/SurfaceListCard/index.js.map +1 -1
- package/dist/core/composite/AvatarButton/index.d.ts +3 -1
- package/dist/core/composite/AvatarButton/index.d.ts.map +1 -1
- package/dist/core/composite/AvatarButton/index.js +1 -1
- package/dist/core/composite/AvatarButton/index.js.map +1 -1
- package/dist/core/composite/AvatarGroup/index.d.ts +11 -3
- package/dist/core/composite/AvatarGroup/index.d.ts.map +1 -1
- package/dist/core/composite/AvatarGroup/index.js +9 -5
- package/dist/core/composite/AvatarGroup/index.js.map +1 -1
- package/dist/core/composite/CheckboxGroup/index.d.ts +2 -2
- package/dist/core/composite/CheckboxGroup/index.d.ts.map +1 -1
- package/dist/core/composite/CheckboxGroup/index.js +5 -1
- package/dist/core/composite/CheckboxGroup/index.js.map +1 -1
- package/dist/core/composite/RadioGroup/index.d.ts +2 -2
- package/dist/core/composite/RadioGroup/index.d.ts.map +1 -1
- package/dist/core/composite/RadioGroup/index.js +5 -1
- package/dist/core/composite/RadioGroup/index.js.map +1 -1
- package/dist/core/composite/Timeline/index.js +1 -1
- package/dist/core/composite/Timeline/index.js.map +1 -1
- package/dist/core/composition/ChatWorkspace/index.d.ts.map +1 -1
- package/dist/core/composition/ChatWorkspace/index.js +2 -1
- package/dist/core/composition/ChatWorkspace/index.js.map +1 -1
- package/dist/core/composition/Sidebar/index.d.ts +1 -1
- package/dist/core/composition/Sidebar/index.d.ts.map +1 -1
- package/dist/core/composition/Sidebar/index.js +10 -4
- package/dist/core/composition/Sidebar/index.js.map +1 -1
- package/dist/core/composition/WorkspaceShell/classNames.d.ts +1 -0
- package/dist/core/composition/WorkspaceShell/classNames.d.ts.map +1 -1
- package/dist/core/composition/WorkspaceShell/classNames.js +1 -0
- package/dist/core/composition/WorkspaceShell/classNames.js.map +1 -1
- package/dist/core/composition/WorkspaceShell/index.d.ts +30 -0
- package/dist/core/composition/WorkspaceShell/index.d.ts.map +1 -1
- package/dist/core/composition/WorkspaceShell/index.js +9 -4
- package/dist/core/composition/WorkspaceShell/index.js.map +1 -1
- package/dist/core/dna.d.ts +5 -0
- package/dist/core/dna.d.ts.map +1 -1
- package/dist/core/dna.js +5 -0
- package/dist/core/dna.js.map +1 -1
- package/dist/core/overlayScope.d.ts +16 -0
- package/dist/core/overlayScope.d.ts.map +1 -1
- package/dist/core/overlayScope.js +12 -0
- package/dist/core/overlayScope.js.map +1 -1
- package/dist/core/primitive/Avatar/index.d.ts +11 -6
- package/dist/core/primitive/Avatar/index.d.ts.map +1 -1
- package/dist/core/primitive/Avatar/index.js +21 -4
- package/dist/core/primitive/Avatar/index.js.map +1 -1
- package/dist/core/primitive/Checkbox/index.d.ts +2 -2
- package/dist/core/primitive/Checkbox/index.d.ts.map +1 -1
- package/dist/core/primitive/Checkbox/index.js +5 -1
- package/dist/core/primitive/Checkbox/index.js.map +1 -1
- package/dist/core/primitive/DateField/index.d.ts +2 -2
- package/dist/core/primitive/DateField/index.d.ts.map +1 -1
- package/dist/core/primitive/DateField/index.js +5 -1
- package/dist/core/primitive/DateField/index.js.map +1 -1
- package/dist/core/primitive/Field/index.d.ts +3 -2
- package/dist/core/primitive/Field/index.d.ts.map +1 -1
- package/dist/core/primitive/Field/index.js.map +1 -1
- package/dist/core/primitive/Input/index.d.ts +2 -1
- package/dist/core/primitive/Input/index.d.ts.map +1 -1
- package/dist/core/primitive/Input/index.js +4 -2
- package/dist/core/primitive/Input/index.js.map +1 -1
- package/dist/core/primitive/NumberField/index.d.ts +2 -2
- package/dist/core/primitive/NumberField/index.d.ts.map +1 -1
- package/dist/core/primitive/NumberField/index.js +5 -1
- package/dist/core/primitive/NumberField/index.js.map +1 -1
- package/dist/core/primitive/PressableField/index.d.ts +4 -1
- package/dist/core/primitive/PressableField/index.d.ts.map +1 -1
- package/dist/core/primitive/PressableField/index.js +5 -1
- package/dist/core/primitive/PressableField/index.js.map +1 -1
- package/dist/core/primitive/RelativeTime/clock.d.ts +5 -0
- package/dist/core/primitive/RelativeTime/clock.d.ts.map +1 -0
- package/dist/core/primitive/RelativeTime/clock.js +28 -0
- package/dist/core/primitive/RelativeTime/clock.js.map +1 -0
- package/dist/core/primitive/RelativeTime/index.d.ts +18 -0
- package/dist/core/primitive/RelativeTime/index.d.ts.map +1 -0
- package/dist/core/primitive/RelativeTime/index.js +45 -0
- package/dist/core/primitive/RelativeTime/index.js.map +1 -0
- package/dist/core/primitive/SearchField/index.d.ts +2 -2
- package/dist/core/primitive/SearchField/index.d.ts.map +1 -1
- package/dist/core/primitive/SearchField/index.js +5 -1
- package/dist/core/primitive/SearchField/index.js.map +1 -1
- package/dist/core/primitive/Switch/index.d.ts +9 -4
- package/dist/core/primitive/Switch/index.d.ts.map +1 -1
- package/dist/core/primitive/Switch/index.js +1 -1
- package/dist/core/primitive/Switch/index.js.map +1 -1
- package/dist/core/primitive/Textarea/index.d.ts +2 -2
- package/dist/core/primitive/Textarea/index.d.ts.map +1 -1
- package/dist/core/primitive/Textarea/index.js +3 -1
- package/dist/core/primitive/Textarea/index.js.map +1 -1
- package/dist/core/primitive/TimeField/index.d.ts +2 -2
- package/dist/core/primitive/TimeField/index.d.ts.map +1 -1
- package/dist/core/primitive/TimeField/index.js +5 -1
- package/dist/core/primitive/TimeField/index.js.map +1 -1
- package/dist/core/surfaceContext.d.ts +11 -0
- package/dist/core/surfaceContext.d.ts.map +1 -0
- package/dist/core/surfaceContext.js +14 -0
- package/dist/core/surfaceContext.js.map +1 -0
- package/dist/offset-pop/components-overlays.css +1 -1
- package/package.json +8 -3
package/README.md
CHANGED
|
@@ -140,20 +140,77 @@ Each group below matches a Storybook group. Run `npm run storybook`: every story
|
|
|
140
140
|
the family and theme chosen in the toolbar. It shows the default, disabled, invalid, pending and
|
|
141
141
|
selected/current states where they apply. Overlay stories open by default.
|
|
142
142
|
|
|
143
|
-
**Primitives (
|
|
143
|
+
**Primitives (27).** Avatar, Badge, Button, CloseButton, Divider, GrammarRoot, Heading, Icon,
|
|
144
144
|
IconButton, IconTile, Image, IncludedMark, Kbd, Label, LeadingNumber, Link, Meter, PlainPressable,
|
|
145
|
-
Progress, ProgressCircle, RankArtwork, Skeleton, Spinner, StateMark, Text, TextAction.
|
|
145
|
+
Progress, ProgressCircle, RankArtwork, RelativeTime, Skeleton, Spinner, StateMark, Text, TextAction.
|
|
146
146
|
|
|
147
147
|
**Forms (24).** ButtonGroup, Checkbox, CheckboxGroup, ComboBox, DateField, DatePicker,
|
|
148
148
|
DateRangePicker, Field, Fieldset, FileDropzone, Form, Input, NumberField, OtpInput,
|
|
149
149
|
PressableField, RadioGroup, Rating, SearchField, SegmentedControl, Select, Slider, Switch,
|
|
150
150
|
Textarea, TimeField. Every Field-contract control takes `label`, `description`, `errorMessage`,
|
|
151
151
|
`isRequired`, `isDisabled`, `isReadOnly` and `isInvalid`. `Select`, `ComboBox`, `SearchField` and
|
|
152
|
-
`Form` also take `isPending`. `OtpInput`
|
|
152
|
+
`Form` also take `isPending`. `OtpInput` names the code with `label` or an existing label's `labelledBy`.
|
|
153
153
|
|
|
154
154
|
**Overlays (9).** Alert, AlertDialog, Dialog, Drawer, DropdownMenu, Popover, Toast, Toaster,
|
|
155
|
-
Tooltip. Dialog, AlertDialog, Drawer, Popover and DropdownMenu
|
|
156
|
-
`isOpen
|
|
155
|
+
Tooltip. Dialog, AlertDialog, Drawer, Popover and DropdownMenu support uncontrolled `defaultOpen`
|
|
156
|
+
and controlled `isOpen` / `onOpenChange` through the shared overlay contract.
|
|
157
|
+
|
|
158
|
+
`DropdownMenuItem` accepts either `iconSource` or `mark`, a `ReactNode` such as a logo; its public
|
|
159
|
+
type rejects both together. Grammar renders either in the same decorative leading slot, with the
|
|
160
|
+
same alignment and icon-size rule. Keep the visible item label as its accessible name and supply
|
|
161
|
+
the mark without visual slot CSS.
|
|
162
|
+
|
|
163
|
+
`RelativeTime` renders `<time dateTime>` for live relative copy such as "5 minutes ago" and a
|
|
164
|
+
countdown such as "in 5 minutes". Pass `dateTime` as a timezone-qualified ISO string and `locale`
|
|
165
|
+
as the active BCP 47 locale. `mode="relative"` is the default; `mode="countdown"` reduces the time
|
|
166
|
+
remaining and settles at the locale's "now" when the target expires. `size` (`xs`, `sm`, `md`) and
|
|
167
|
+
`tone` (`default`, `muted`, `accent`) use Common's text presentation.
|
|
168
|
+
|
|
169
|
+
```tsx
|
|
170
|
+
<RelativeTime dateTime="2026-10-25T09:00:00Z" locale="en" />
|
|
171
|
+
<RelativeTime dateTime="2026-10-25T10:00:00+01:00" locale="en" mode="countdown" />
|
|
172
|
+
```
|
|
173
|
+
|
|
174
|
+
The server and initial hydration render show `fallback`, or the canonical ISO instant when it is
|
|
175
|
+
omitted. After hydration, every instance reads the same shared ticker. The first subscriber starts
|
|
176
|
+
it and the last unsubscribe stops it; leaves own no clock or refresh interval. Supply translated
|
|
177
|
+
`fallback` copy when an ISO instant is unsuitable. Time changes are not a live announcement, so a
|
|
178
|
+
page of timestamps does not repeatedly interrupt assistive technology. Grammar owns native
|
|
179
|
+
`Intl.RelativeTimeFormat` for this primitive; other application date display follows FE-DATE.
|
|
180
|
+
|
|
181
|
+
**DropdownMenu entries.** `entries` is a list of items and sections; the app writes data and no markup, no separator, no
|
|
182
|
+
className and no size. An item takes `id`, `label`, `description`, `shortcut`, `href`, `isDisabled`, `submenu` and:
|
|
183
|
+
|
|
184
|
+
- `iconSource`: a glyph component (the app's Heroicons). Grammar draws it in HeroUI's leading position at the canon icon size,
|
|
185
|
+
20px (`size-5`, `Icon` `leading`; the Heroicons rule R93 / FE-ICON). HeroUI's own docs example draws its sample glyph at
|
|
186
|
+
`size-4`, which is example content and not a component default, so 20px is StarCi's icon rule and not a vendor override.
|
|
187
|
+
- `leading`: a node instead of a glyph, e.g. an identity mark (`<Avatar size="sm" shape="rounded" isDecorative />`).
|
|
188
|
+
- `tone: "danger"`: HeroUI's `danger` variant, which colours the label and the glyph. Sign out, delete, remove, leave workspace
|
|
189
|
+
and revoke are destructive; the lint rule `starci-fe/destructive-menu-entry-needs-danger-tone` refuses such an entry without it.
|
|
190
|
+
|
|
191
|
+
A section takes `label` and optionally `selection` (`mode: "single" | "multiple"`, `selectedIds`, `onChange`). Between two
|
|
192
|
+
adjacent entries, when either is a section, Grammar always inserts HeroUI's own `Separator`: the app cannot forget one. The selection
|
|
193
|
+
mark is HeroUI's `Dropdown.ItemIndicator` in its default placement (the check sits at the start of the item, and at the end only for
|
|
194
|
+
an item with a submenu).
|
|
195
|
+
|
|
196
|
+
```tsx
|
|
197
|
+
<DropdownMenu
|
|
198
|
+
trigger={<AvatarButton name="Alex Morgan" />}
|
|
199
|
+
placement="bottom end"
|
|
200
|
+
entries={[
|
|
201
|
+
{ kind: "section", id: "workspace", label: "Workspace", selection: { mode: "single", selectedIds, onChange },
|
|
202
|
+
items: [{ id: "hakuen", label: "Hakuen", description: "Owner", leading: <Avatar name="Hakuen" size="sm" shape="rounded" isDecorative /> }] },
|
|
203
|
+
{ kind: "section", id: "account", items: [{ id: "profile", label: "Profile", description: "Name and photo", iconSource: UserIcon }] },
|
|
204
|
+
{ kind: "section", id: "session", items: [{ id: "sign-out", label: "Sign out", tone: "danger", iconSource: ArrowRightStartOnRectangleIcon }] },
|
|
205
|
+
]}
|
|
206
|
+
/>
|
|
207
|
+
```
|
|
208
|
+
|
|
209
|
+
**HeroUI parity.** Grammar is a wrapper of HeroUI, so a wrapper keeps HeroUI's anatomy and defaults. The vendor reference
|
|
210
|
+
(`knowledge/vendor/heroui/<component>.yaml` in the StarCi runtime) is measured from the installed `@heroui/react` and
|
|
211
|
+
`@heroui/styles`; the gate `GRAMMAR_VENDOR_PARITY` fails a wrapper that renders a part HeroUI does not have, omits one it
|
|
212
|
+
requires, restyles what the vendor decides, or replaces a vendor default, unless an owner-pinned override declares it. `ListBox`
|
|
213
|
+
and `Select` are HeroUI's own list-box items (a `Label`, a `Description`, the item indicator at the end of the row).
|
|
157
214
|
|
|
158
215
|
**Navigation & Data (14).** Accordion, AvatarGroup, Breadcrumbs, Calendar, DataTable,
|
|
159
216
|
DescriptionList, Disclosure, ListBox, Pagination, Stepper, Subnav, Tabs, TagGroup, Timeline.
|
|
@@ -192,6 +249,17 @@ defines the native loading and caption behaviour (reviewed 2026-10-18).
|
|
|
192
249
|
`thumbIcon` / `selectedThumbIcon` for a glyph inside the thumb, so a sun/moon theme switch is
|
|
193
250
|
`<Switch size="lg" label="Dark mode" thumbIcon={Sun} selectedThumbIcon={Moon} />`.
|
|
194
251
|
|
|
252
|
+
`Switch size="control"` and `Avatar` / `AvatarButton size="control"` share the control height (2.75rem, the
|
|
253
|
+
IconButton of a bar), so a language button, a theme switch, a bell and an avatar line up at one height; `Switch
|
|
254
|
+
isLabelHidden` keeps its label as the accessible name and draws nothing. `WorkspaceShell viewport="fixed"` is one
|
|
255
|
+
viewport tall (`100dvh`) with the header on top and the navigation and primary regions scrolling on their own, so an
|
|
256
|
+
app writes no viewport arithmetic; `navigationDrawer` adds the compact-width opener row (the bar's height, an
|
|
257
|
+
edge-to-edge divider, a round tertiary icon button) and its left Sidebar drawer. Pass `defaultOpen`
|
|
258
|
+
to let the Drawer own its state; no state hook or callback is needed in the layout. The shell
|
|
259
|
+
reuses the shared overlay contract, so optional `isOpen` / `onOpenChange` remain available for
|
|
260
|
+
coordinated client interactions. The opener is the Drawer's own trigger, which owns activation,
|
|
261
|
+
dismissal and focus return.
|
|
262
|
+
|
|
195
263
|
`PlainPressable` is a pressable that does not look like a button: no padding, background, border or
|
|
196
264
|
shape in any state, no hover effect, a zoom-out on press, a focus ring, and no zoom under
|
|
197
265
|
`prefers-reduced-motion`. `AvatarButton` is a circle-only avatar built on it (an account-menu
|
|
@@ -199,6 +267,22 @@ trigger) and `IdentityLockup` a mark beside a title and a muted subtitle with no
|
|
|
199
267
|
switcher trigger). All three work as the `trigger` of `DropdownMenu` and `Popover`. `IdentityLockup`'s mark is a transparent, uncropped square by default (`markShape="rounded"` opts in to a
|
|
200
268
|
corner) and `Avatar` takes `shape="square" | "rounded"`.
|
|
201
269
|
|
|
270
|
+
Personal avatars are circles. Pass the user or identity id as `seed` on `Avatar`, `AvatarButton`
|
|
271
|
+
or each `AvatarGroup` member. Grammar renders the fixed Lorelei DiceBear style chosen by its DNA,
|
|
272
|
+
memoized for that seed with identical server and client output. An uploaded `src` wins; with
|
|
273
|
+
neither `src` nor `seed`, the existing initials fallback remains. Images have `alt=""`; the avatar
|
|
274
|
+
shell carries its name, or `isDecorative` hides it beside a visible name. `AvatarButton` names the
|
|
275
|
+
button and keeps its inner avatar decorative. Apps install and import Grammar; its pinned
|
|
276
|
+
DiceBear runtime dependencies and avatar drawing stay inside the package.
|
|
277
|
+
The package requires Node 22 or newer, as does the pinned DiceBear engine.
|
|
278
|
+
|
|
279
|
+
```tsx
|
|
280
|
+
<Avatar name={user.name} seed={user.id} isDecorative />
|
|
281
|
+
<AvatarButton name={user.name} seed={user.id} src={user.avatarUrl} />
|
|
282
|
+
<AvatarGroup label="Team" items={members.map(user => ({ id: user.id, name: user.name, seed: user.id }))}
|
|
283
|
+
overflowLabel={count => `${count} more members`} />
|
|
284
|
+
```
|
|
285
|
+
|
|
202
286
|
**Surfaces (14).** EmptyNotice, FencedCodeBlock, HorizontalScrollRegion, MarkdownArticle,
|
|
203
287
|
MarkdownTableFrame, MediaFrame, Rail, SectionHeader, StaticStateRow, SurfaceAccordionCard,
|
|
204
288
|
SurfaceCard, SurfaceCopyGroup, SurfaceListCard, VerticalScrollRegion.
|
|
@@ -213,6 +297,42 @@ with TopBar, BottomNav and a Toast.
|
|
|
213
297
|
0.5.0 groups. `defineGrammarFamily` builds a new family over the registry. It may replace
|
|
214
298
|
renderers with prop-compatible versions and add extensions whose names don't collide with Common's.
|
|
215
299
|
|
|
300
|
+
### Surface controls, panel lists and group identity
|
|
301
|
+
|
|
302
|
+
SurfaceCard (including `frame="frameless"`), SurfaceListCard, SurfaceAccordionCard, Rail, Sidebar
|
|
303
|
+
and the content of Drawer, Dialog, AlertDialog and Popover supply the nesting context of ANATOMY-2.
|
|
304
|
+
Omit a field's `variant`: Input, SearchField, Textarea, NumberField, DateField, TimeField, Select,
|
|
305
|
+
ComboBox, DatePicker, DateRangePicker, Checkbox, CheckboxGroup, RadioGroup, PressableField and OtpInput use `secondary`
|
|
306
|
+
inside these surfaces and `primary` outside. Overlay triggers keep their surrounding context.
|
|
307
|
+
The FE canon's `FE_NESTED_VARIANT` rejects an explicit primary field inside a surface and raw
|
|
308
|
+
`bg-surface` paint in application classNames.
|
|
309
|
+
|
|
310
|
+
ListBox defaults to `inset="panel"` inside the same context. The list container extends into the
|
|
311
|
+
panel gutter so row content, loading content and section captions align with the header and search
|
|
312
|
+
field without padding wrappers (PADDING-0). Items keep HeroUI's default padding and selection-indicator
|
|
313
|
+
space; row backgrounds extend into the gutter. Outside a surface, the vendor list inset stays;
|
|
314
|
+
`inset="panel"` also selects the alignment explicitly. Pass flat `items` or named `sections`
|
|
315
|
+
(`ListBoxSection`), with unique item ids across the whole list. `FE_LIST_PANEL_PADDING` rejects
|
|
316
|
+
horizontal padding wrappers around a list or its section caption. Sidebar's expanded and collapsed
|
|
317
|
+
rows also retain HeroUI item padding.
|
|
318
|
+
|
|
319
|
+
```tsx
|
|
320
|
+
<SurfaceCard ariaLabel="Conversations">
|
|
321
|
+
<Heading level={2}>Conversations</Heading>
|
|
322
|
+
<SearchField label="Find a conversation" />
|
|
323
|
+
<ListBox label="Conversations" sections={[
|
|
324
|
+
{ id: "recent", label: "Recent", items: [{ id: "team", label: "Design team" }] },
|
|
325
|
+
]} />
|
|
326
|
+
</SurfaceCard>
|
|
327
|
+
```
|
|
328
|
+
|
|
329
|
+
For a conversation or group identity, use `AvatarGroup layout="grid"` (COLLECTION-3, the Zalo
|
|
330
|
+
reference). It is one round Avatar-sized mark at `sm`, `md`, `lg` or `control`: two members split
|
|
331
|
+
the circle, three show one above two, and four use a 2 by 2 grid with a hairline gap. With more
|
|
332
|
+
members, the fourth cell shows `+N` for everyone beyond the first three. Each member uses Avatar's
|
|
333
|
+
uploaded image, `seed` and initials fallback, and accepts the same custom `fallback`. A single
|
|
334
|
+
person uses Avatar. The default `layout="row"` keeps the overlapping member row.
|
|
335
|
+
|
|
216
336
|
## Theming
|
|
217
337
|
|
|
218
338
|
- **Tokens.** Each family writes its DNA as CSS custom properties on its root: `--starci-core-*`,
|
|
@@ -224,8 +344,8 @@ renderers with prop-compatible versions and add extensions whose names don't col
|
|
|
224
344
|
`--starci-core-surface-tertiary`; HeroUI's `--surface-tertiary` is left to Common and is never
|
|
225
345
|
re-bound by the family. To adjust a family, override its tokens on the
|
|
226
346
|
root, for example `<CoreGrammarRoot style={{ "--starci-core-accent": "#2f6bff" }}>`. The
|
|
227
|
-
`*_TOKEN_NAMES` / `*_TOKEN_DEFAULTS` exports are the supported list.
|
|
228
|
-
|
|
347
|
+
`*_TOKEN_NAMES` / `*_TOKEN_DEFAULTS` exports are the supported list. The DNA generator measures
|
|
348
|
+
their packaged values; callers own their token overrides.
|
|
229
349
|
- **Spacing.** Every family uses the Common 4px scale (`COMMON_SPACING_SCALE`,
|
|
230
350
|
`COMMON_SPACING_TOKENS`), and Common's `--grammar-*-gap` / `--grammar-page-inset` variables.
|
|
231
351
|
- **Dark.** `theme` on any root takes `"light"`, `"dark"` or `"system"` (the default), which sets
|
package/dist/.build-stamp.json
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
{
|
|
2
2
|
"schema": "starci/grammar-build-stamp@1",
|
|
3
3
|
"package": "@starci/grammar",
|
|
4
|
-
"version": "0.
|
|
4
|
+
"version": "0.12.0",
|
|
5
5
|
"algorithm": "starci/grammar-source-digest@1",
|
|
6
|
-
"sourceDigest": "sha256:
|
|
7
|
-
"distDigest": "sha256:
|
|
8
|
-
"builtAt": "2026-10-
|
|
6
|
+
"sourceDigest": "sha256:fd2b712405d87230cd1736101068d562483cd3c0b8b4ef4d629bb9944354842f",
|
|
7
|
+
"distDigest": "sha256:1a7c5eb6dcb25a1ef5bfba338d3c374fce07a3d1f534fe8f830b66e2ca1893b3",
|
|
8
|
+
"builtAt": "2026-10-25T11:48:36.769Z"
|
|
9
9
|
}
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
@layer starci-grammar-common {
|
|
2
|
+
/* The vendor Avatar shell supplies the exact single-avatar dimensions, including the control size. */
|
|
3
|
+
.starci-core-avatar-group[data-grammar-avatar-layout="grid"] {
|
|
4
|
+
--starci-avatar-group-text-size: calc(var(--avatar-size, 2.5rem) / 4);
|
|
5
|
+
overflow: hidden;
|
|
6
|
+
border-radius: 50%;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
.starci-core-avatar-group-grid {
|
|
10
|
+
display: grid;
|
|
11
|
+
grid-template-columns: repeat(2, minmax(0, 1fr));
|
|
12
|
+
grid-template-rows: repeat(2, minmax(0, 1fr));
|
|
13
|
+
gap: 1px;
|
|
14
|
+
inline-size: 100%;
|
|
15
|
+
block-size: 100%;
|
|
16
|
+
background: var(--surface, Canvas);
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
.starci-core-avatar-group[data-grammar-avatar-cells="1"] > .starci-core-avatar-group-grid {
|
|
20
|
+
grid-template-columns: minmax(0, 1fr);
|
|
21
|
+
grid-template-rows: minmax(0, 1fr);
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
.starci-core-avatar-group[data-grammar-avatar-cells="2"] > .starci-core-avatar-group-grid {
|
|
25
|
+
grid-template-rows: minmax(0, 1fr);
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
/* Zalo's three-member identity has one full-width member above the two remaining members. */
|
|
29
|
+
.starci-core-avatar-group[data-grammar-avatar-cells="3"] .starci-core-avatar-group-member:first-child {
|
|
30
|
+
grid-column: 1 / -1;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
.starci-core-avatar-group-grid > .starci-core-avatar-group-member {
|
|
34
|
+
inline-size: 100% !important;
|
|
35
|
+
block-size: 100% !important;
|
|
36
|
+
min-inline-size: 0;
|
|
37
|
+
min-block-size: 0;
|
|
38
|
+
border-radius: 0 !important;
|
|
39
|
+
font-size: inherit;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
.starci-core-avatar-group-member > * {
|
|
43
|
+
border-radius: 0 !important;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
.starci-core-avatar-group:not([data-grammar-avatar-cells="1"]) .starci-core-avatar-group-member [data-grammar-avatar-fallback] {
|
|
47
|
+
font-size: var(--starci-avatar-group-text-size) !important;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
.starci-core-avatar-group-member img {
|
|
51
|
+
object-fit: cover;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
.starci-core-avatar-group-count {
|
|
55
|
+
display: flex;
|
|
56
|
+
align-items: center;
|
|
57
|
+
justify-content: center;
|
|
58
|
+
min-inline-size: 0;
|
|
59
|
+
background: var(--surface-secondary, Canvas);
|
|
60
|
+
color: var(--foreground, CanvasText);
|
|
61
|
+
font-size: var(--starci-avatar-group-text-size);
|
|
62
|
+
font-weight: 600;
|
|
63
|
+
line-height: 1;
|
|
64
|
+
}
|
|
65
|
+
}
|
|
@@ -145,7 +145,10 @@
|
|
|
145
145
|
border-radius: var(--field-radius, calc(var(--radius, 0.5rem) * 1.5));
|
|
146
146
|
}
|
|
147
147
|
|
|
148
|
-
.starci-core-field-skeleton-control:is([data-grammar-field-skeleton="
|
|
148
|
+
.starci-core-field-skeleton-control:is([data-grammar-field-skeleton="input"]) { block-size: 2.5rem; }
|
|
149
|
+
|
|
150
|
+
/* The select trigger is HeroUI's own (min-h-9). */
|
|
151
|
+
.starci-core-field-skeleton-control[data-grammar-field-skeleton="select"] { block-size: 2.25rem; }
|
|
149
152
|
|
|
150
153
|
/* Joined segments: one block, a segment-width (5rem) per option, never wider than the field; `fill` takes the whole row. */
|
|
151
154
|
.starci-core-field-skeleton-control[data-grammar-field-skeleton="segmented"] {
|
|
@@ -161,6 +164,7 @@
|
|
|
161
164
|
.starci-core-field-skeleton-control[data-grammar-field-skeleton="switch"] { flex: none; block-size: 1.25rem; inline-size: 2rem; }
|
|
162
165
|
.starci-core-field-skeleton-control[data-grammar-field-skeleton="switch"] > .starci-core-skeleton { border-radius: 999px; }
|
|
163
166
|
.starci-core-field-skeleton-control[data-grammar-field-skeleton="switch"][data-grammar-switch-size="lg"] { block-size: 2.25rem; inline-size: 4rem; }
|
|
167
|
+
.starci-core-field-skeleton-control[data-grammar-field-skeleton="switch"][data-grammar-switch-size="control"] { block-size: var(--starci-core-control-min-size, 2.75rem); inline-size: 4.875rem; }
|
|
164
168
|
|
|
165
169
|
.starci-core-field-skeleton-choice-label {
|
|
166
170
|
color: var(--foreground, CanvasText);
|
|
@@ -178,7 +182,6 @@
|
|
|
178
182
|
}
|
|
179
183
|
}
|
|
180
184
|
|
|
181
|
-
.starci-core-select-trigger,
|
|
182
185
|
.starci-core-combo-box-group,
|
|
183
186
|
.starci-core-search-field-group,
|
|
184
187
|
.starci-core-number-field-group,
|
|
@@ -187,7 +190,6 @@
|
|
|
187
190
|
min-width: 0;
|
|
188
191
|
}
|
|
189
192
|
|
|
190
|
-
.starci-core-select-value,
|
|
191
193
|
.starci-core-combo-box-input,
|
|
192
194
|
.starci-core-search-field-input,
|
|
193
195
|
.starci-core-number-field-input {
|
|
@@ -203,7 +205,6 @@
|
|
|
203
205
|
* inside inherits the corner (no square right side behind the trigger) and stretches to the group's height.
|
|
204
206
|
*/
|
|
205
207
|
.starci-core-input-field,
|
|
206
|
-
.starci-core-select-trigger,
|
|
207
208
|
.starci-core-combo-box-group,
|
|
208
209
|
.starci-core-search-field-group,
|
|
209
210
|
.starci-core-number-field-group,
|
|
@@ -236,11 +237,6 @@
|
|
|
236
237
|
}
|
|
237
238
|
}
|
|
238
239
|
|
|
239
|
-
.starci-core-select-trigger {
|
|
240
|
-
align-items: center;
|
|
241
|
-
padding-inline: 0.75rem 1.75rem;
|
|
242
|
-
}
|
|
243
|
-
|
|
244
240
|
.starci-core-combo-box-group {
|
|
245
241
|
align-items: stretch;
|
|
246
242
|
}
|
|
@@ -258,40 +254,6 @@
|
|
|
258
254
|
padding-inline-end: 0.5rem;
|
|
259
255
|
}
|
|
260
256
|
|
|
261
|
-
/*
|
|
262
|
-
* The selected value of a Select stays ONE line and keeps the field height: it ellipsizes (the trigger carries the
|
|
263
|
-
* full text as its title) and shows only the option's label, never its description. Popover options still wrap.
|
|
264
|
-
*/
|
|
265
|
-
.starci-core-select-value {
|
|
266
|
-
flex: 1 1 0;
|
|
267
|
-
overflow: hidden;
|
|
268
|
-
overflow-wrap: normal;
|
|
269
|
-
text-overflow: ellipsis;
|
|
270
|
-
white-space: nowrap;
|
|
271
|
-
}
|
|
272
|
-
|
|
273
|
-
.starci-core-select-value-text {
|
|
274
|
-
display: block;
|
|
275
|
-
min-width: 0;
|
|
276
|
-
overflow: hidden;
|
|
277
|
-
text-overflow: ellipsis;
|
|
278
|
-
}
|
|
279
|
-
|
|
280
|
-
.starci-core-select-value .starci-core-list-option-copy {
|
|
281
|
-
display: block;
|
|
282
|
-
overflow: hidden;
|
|
283
|
-
text-overflow: ellipsis;
|
|
284
|
-
}
|
|
285
|
-
|
|
286
|
-
.starci-core-select-value .starci-core-list-option-label {
|
|
287
|
-
overflow-wrap: normal;
|
|
288
|
-
}
|
|
289
|
-
|
|
290
|
-
.starci-core-select-value .starci-core-list-option-description,
|
|
291
|
-
.starci-core-select-value [data-slot="list-box-item-indicator"] {
|
|
292
|
-
display: none;
|
|
293
|
-
}
|
|
294
|
-
|
|
295
257
|
.starci-core-date-input {
|
|
296
258
|
min-width: 0;
|
|
297
259
|
flex: 1;
|
|
@@ -303,22 +265,14 @@
|
|
|
303
265
|
color: var(--muted, GrayText);
|
|
304
266
|
}
|
|
305
267
|
|
|
306
|
-
/* --- List popover (Select, ComboBox) ---
|
|
307
|
-
|
|
268
|
+
/* --- List popover (Select, ComboBox) ---
|
|
269
|
+
* An option is HeroUI's own: a `Label`, with a `Description` under it inside one column when it has one, then the
|
|
270
|
+
* item indicator. The column is the only Grammar element; it lays out and paints nothing. */
|
|
271
|
+
[data-grammar-option-copy] {
|
|
308
272
|
display: flex;
|
|
309
273
|
min-width: 0;
|
|
310
274
|
flex: 1;
|
|
311
275
|
flex-direction: column;
|
|
312
|
-
gap: 0.25rem;
|
|
313
|
-
}
|
|
314
|
-
|
|
315
|
-
.starci-core-list-option-label {
|
|
316
|
-
overflow-wrap: anywhere;
|
|
317
|
-
}
|
|
318
|
-
|
|
319
|
-
.starci-core-list-option-description {
|
|
320
|
-
color: var(--muted, GrayText);
|
|
321
|
-
font-size: 0.75rem;
|
|
322
276
|
}
|
|
323
277
|
|
|
324
278
|
.starci-core-list-empty {
|
|
@@ -356,44 +310,75 @@
|
|
|
356
310
|
}
|
|
357
311
|
|
|
358
312
|
/*
|
|
359
|
-
* --- Switch
|
|
360
|
-
*
|
|
361
|
-
*
|
|
362
|
-
*
|
|
363
|
-
*
|
|
364
|
-
*
|
|
313
|
+
* --- Switch sizes "lg" (the StarCi look) and "control" ---
|
|
314
|
+
* `lg`: a 4rem by 2.25rem fully round track, a thumb the track height less 0.5rem inset 0.25rem, an optional glyph centred in the
|
|
315
|
+
* thumb. `control`: the same shape at the shared control height (2.75rem), so a theme switch lines up with the IconButtons and the
|
|
316
|
+
* avatar of a bar. Keyed on the stamps the component emits (`data-grammar-switch-size`, `data-grammar-switch-thumb`,
|
|
317
|
+
* `data-grammar-switch-icon`), never on vendor class names. The geometry is `!important` because consumers import this sheet on
|
|
318
|
+
* either side of `@heroui/styles`, so layer rank cannot decide against the vendor `.switch--*` size rules. The on position is a
|
|
319
|
+
* margin (as HeroUI's own thumb), the percentage resolving against the track: 100% - thumb - inset.
|
|
365
320
|
*/
|
|
366
|
-
[data-component="Switch"][data-grammar-switch-size="lg"]
|
|
367
|
-
|
|
368
|
-
|
|
321
|
+
[data-component="Switch"][data-grammar-switch-size="lg"] {
|
|
322
|
+
--grammar-switch-height: 2.25rem;
|
|
323
|
+
--grammar-switch-width: 4rem;
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
[data-component="Switch"][data-grammar-switch-size="control"] {
|
|
327
|
+
--grammar-switch-height: var(--starci-core-control-min-size, 2.75rem);
|
|
328
|
+
--grammar-switch-width: 4.875rem;
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
[data-component="Switch"]:is([data-grammar-switch-size="lg"], [data-grammar-switch-size="control"]) {
|
|
332
|
+
--grammar-switch-thumb: calc(var(--grammar-switch-height) - 0.5rem);
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
[data-component="Switch"]:is([data-grammar-switch-size="lg"], [data-grammar-switch-size="control"]) [data-grammar-choice-control="switch"] {
|
|
336
|
+
block-size: var(--grammar-switch-height) !important;
|
|
337
|
+
inline-size: var(--grammar-switch-width) !important;
|
|
369
338
|
border-radius: 9999px !important;
|
|
370
339
|
}
|
|
371
340
|
|
|
372
|
-
[data-component="Switch"][data-grammar-switch-size="lg"] [data-grammar-switch-thumb] {
|
|
373
|
-
block-size:
|
|
374
|
-
inline-size:
|
|
341
|
+
[data-component="Switch"]:is([data-grammar-switch-size="lg"], [data-grammar-switch-size="control"]) [data-grammar-switch-thumb] {
|
|
342
|
+
block-size: var(--grammar-switch-thumb) !important;
|
|
343
|
+
inline-size: var(--grammar-switch-thumb) !important;
|
|
375
344
|
border-radius: 9999px !important;
|
|
376
345
|
margin-inline-start: 0.25rem !important;
|
|
377
346
|
align-items: center;
|
|
378
347
|
justify-content: center;
|
|
379
348
|
}
|
|
380
349
|
|
|
381
|
-
[data-component="Switch"][data-grammar-switch-size="lg"]:is([data-selected="true"], [aria-checked="true"]) [data-grammar-switch-thumb] {
|
|
382
|
-
margin-inline-start: calc(100% -
|
|
350
|
+
[data-component="Switch"]:is([data-grammar-switch-size="lg"], [data-grammar-switch-size="control"]):is([data-selected="true"], [aria-checked="true"]) [data-grammar-switch-thumb] {
|
|
351
|
+
margin-inline-start: calc(100% - var(--grammar-switch-thumb) - 0.25rem) !important;
|
|
383
352
|
}
|
|
384
353
|
|
|
385
354
|
/* The description and error align under the label, past the wider track. */
|
|
386
|
-
[data-component="Switch"][data-grammar-switch-size="lg"] > [data-slot="description"],
|
|
387
|
-
|
|
388
|
-
padding-inline-start: calc(4rem + 0.75rem);
|
|
355
|
+
[data-component="Switch"]:is([data-grammar-switch-size="lg"], [data-grammar-switch-size="control"]) > :is([data-slot="description"], [data-slot="field-error"]) {
|
|
356
|
+
padding-inline-start: calc(var(--grammar-switch-width) + 0.75rem);
|
|
389
357
|
}
|
|
390
358
|
|
|
359
|
+
/*
|
|
360
|
+
* The thumb glyph box is the canon icon size, not whatever the vendor's thumb container leaves: 20px (size-5, `Icon` `leading`) on `lg`
|
|
361
|
+
* and `control`, 16px (`chip`) on `md`, whose thumb is 1rem tall. The glyph fills its box, centred in the thumb.
|
|
362
|
+
*/
|
|
391
363
|
.starci-core-switch-icon {
|
|
392
364
|
display: inline-flex;
|
|
365
|
+
flex: none;
|
|
366
|
+
inline-size: 1.25rem;
|
|
367
|
+
block-size: 1.25rem;
|
|
393
368
|
align-items: center;
|
|
394
369
|
justify-content: center;
|
|
395
370
|
}
|
|
396
371
|
|
|
372
|
+
[data-component="Switch"][data-grammar-switch-size="md"] .starci-core-switch-icon {
|
|
373
|
+
inline-size: 1rem;
|
|
374
|
+
block-size: 1rem;
|
|
375
|
+
}
|
|
376
|
+
|
|
377
|
+
.starci-core-switch-icon > .starci-core-icon {
|
|
378
|
+
inline-size: 100%;
|
|
379
|
+
block-size: 100%;
|
|
380
|
+
}
|
|
381
|
+
|
|
397
382
|
/* A swapping pair shows the glyph of the current state only; a single glyph always shows. */
|
|
398
383
|
[data-component="Switch"][data-grammar-switch-icons="swap"] [data-grammar-switch-icon="on"] { display: none; }
|
|
399
384
|
[data-component="Switch"][data-grammar-switch-icons="swap"]:is([data-selected="true"], [aria-checked="true"]) [data-grammar-switch-icon="on"] { display: inline-flex; }
|
|
@@ -575,7 +560,7 @@
|
|
|
575
560
|
}
|
|
576
561
|
|
|
577
562
|
/* --- Read-only reads as settled, not as unavailable --- */
|
|
578
|
-
[data-grammar-field="true"][data-grammar-readonly="true"] :where(.starci-core-
|
|
563
|
+
[data-grammar-field="true"][data-grammar-readonly="true"] :where(.starci-core-combo-box-group, .starci-core-search-field-group, .starci-core-number-field-group, .starci-core-date-group, .starci-core-textarea-control) {
|
|
579
564
|
background: var(--surface-secondary, var(--field-background, Canvas));
|
|
580
565
|
}
|
|
581
566
|
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/* PADDING-0: align panel content through the container, retaining HeroUI item and header padding. */
|
|
2
|
+
@layer starci-grammar-common {
|
|
3
|
+
.grammar-common-root .starci-core-list-box[data-grammar-list-inset="panel"] {
|
|
4
|
+
/* Row backgrounds extend into the gutter; vendor padding returns their content to the panel edge. */
|
|
5
|
+
inline-size: calc(100% + 1rem);
|
|
6
|
+
margin-inline: -0.5rem;
|
|
7
|
+
padding-inline: 0 !important;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
.grammar-common-root .starci-core-list-box[data-grammar-list-inset="panel"] .starci-core-list-box-empty {
|
|
11
|
+
padding-inline: 0.5rem;
|
|
12
|
+
}
|
|
13
|
+
}
|
|
@@ -652,6 +652,18 @@
|
|
|
652
652
|
flex: 0 0 auto;
|
|
653
653
|
}
|
|
654
654
|
|
|
655
|
+
/* The control size: the shared control height (2.75rem) over the vendor `lg` avatar, so it lines up with the IconButtons of a bar. */
|
|
656
|
+
.starci-core-avatar[data-grammar-avatar-size="control"] {
|
|
657
|
+
--avatar-size: var(--starci-core-control-min-size, 2.75rem);
|
|
658
|
+
block-size: var(--starci-core-control-min-size, 2.75rem) !important;
|
|
659
|
+
inline-size: var(--starci-core-control-min-size, 2.75rem) !important;
|
|
660
|
+
}
|
|
661
|
+
|
|
662
|
+
.starci-core-avatar-skeleton[data-grammar-avatar-size="control"] > * {
|
|
663
|
+
block-size: var(--starci-core-control-min-size, 2.75rem);
|
|
664
|
+
inline-size: var(--starci-core-control-min-size, 2.75rem);
|
|
665
|
+
}
|
|
666
|
+
|
|
655
667
|
/* A loading avatar (knowledge STATE-8): the Skeleton circle at the avatar's size. */
|
|
656
668
|
.starci-core-avatar-skeleton { display: inline-flex; }
|
|
657
669
|
|
|
@@ -1163,46 +1175,32 @@
|
|
|
1163
1175
|
min-width: 0;
|
|
1164
1176
|
}
|
|
1165
1177
|
|
|
1166
|
-
|
|
1167
|
-
|
|
1168
|
-
|
|
1169
|
-
|
|
1170
|
-
|
|
1171
|
-
display: flex;
|
|
1172
|
-
min-width: 0;
|
|
1173
|
-
flex: 1 1 auto;
|
|
1174
|
-
align-items: center;
|
|
1175
|
-
gap: var(--grammar-row-gap, 0.75rem);
|
|
1176
|
-
}
|
|
1177
|
-
|
|
1178
|
-
.starci-core-list-box-item[data-selected="true"],
|
|
1179
|
-
.starci-core-list-box-item:has(> [data-grammar-selected="true"]) {
|
|
1180
|
-
background: var(--accent-soft, Highlight);
|
|
1181
|
-
color: var(--accent-soft-foreground, HighlightText);
|
|
1182
|
-
}
|
|
1183
|
-
|
|
1184
|
-
.starci-core-list-box-copy {
|
|
1178
|
+
/*
|
|
1179
|
+
* A row is HeroUI's own item (padding, radius, hover, selected indicator at the end). Grammar lays out only what the item
|
|
1180
|
+
* leaves open: the label/description column and the leading and trailing nodes.
|
|
1181
|
+
*/
|
|
1182
|
+
[data-grammar-list-copy] {
|
|
1185
1183
|
display: flex;
|
|
1186
1184
|
min-width: 0;
|
|
1187
1185
|
flex: 1 1 auto;
|
|
1188
1186
|
flex-direction: column;
|
|
1189
1187
|
}
|
|
1190
1188
|
|
|
1191
|
-
|
|
1192
|
-
|
|
1193
|
-
|
|
1194
|
-
|
|
1189
|
+
[data-grammar-list-leading],
|
|
1190
|
+
[data-grammar-list-trailing] {
|
|
1191
|
+
display: inline-flex;
|
|
1192
|
+
flex: 0 0 auto;
|
|
1195
1193
|
}
|
|
1196
1194
|
|
|
1197
|
-
|
|
1198
|
-
|
|
1199
|
-
font-size: 0.8125rem;
|
|
1195
|
+
[data-grammar-list-trailing] {
|
|
1196
|
+
margin-inline-start: auto;
|
|
1200
1197
|
}
|
|
1201
1198
|
|
|
1202
|
-
.
|
|
1203
|
-
|
|
1204
|
-
|
|
1205
|
-
|
|
1199
|
+
/* The touch floor: a row is at least 44px tall on a coarse pointer or a narrow viewport (a minimum, which the vendor never sets). */
|
|
1200
|
+
@media (pointer: coarse), (max-width: 40rem) {
|
|
1201
|
+
.starci-core-list-box-item {
|
|
1202
|
+
min-block-size: var(--starci-core-control-min-size, 2.75rem);
|
|
1203
|
+
}
|
|
1206
1204
|
}
|
|
1207
1205
|
|
|
1208
1206
|
/* Loading list (knowledge STATE-8): the stack of skeleton options, in the same column the real options use. */
|
|
@@ -1215,8 +1213,9 @@
|
|
|
1215
1213
|
/* Loading options (knowledge STATE-8): a label line and a description line, at the loaded option's height. */
|
|
1216
1214
|
.starci-core-list-box-skeleton-option {
|
|
1217
1215
|
display: flex;
|
|
1216
|
+
min-block-size: 2.25rem;
|
|
1218
1217
|
align-items: center;
|
|
1219
|
-
padding-inline: 0.
|
|
1218
|
+
padding-inline: 0.5rem;
|
|
1220
1219
|
padding-block: 0.375rem;
|
|
1221
1220
|
pointer-events: none;
|
|
1222
1221
|
}
|
|
@@ -1549,7 +1548,7 @@
|
|
|
1549
1548
|
.starci-core-bottom-nav-item[data-grammar-current="true"],
|
|
1550
1549
|
.starci-core-step[data-grammar-current="true"] .starci-core-step-marker,
|
|
1551
1550
|
.starci-core-data-table-row[data-grammar-selected="true"],
|
|
1552
|
-
.starci-core-list-box-item
|
|
1551
|
+
.starci-core-list-box-item[data-selected="true"],
|
|
1553
1552
|
.starci-core-tag:has(> [data-grammar-selected="true"]) {
|
|
1554
1553
|
outline: 2px solid Highlight;
|
|
1555
1554
|
outline-offset: -2px;
|
|
@@ -404,10 +404,35 @@
|
|
|
404
404
|
color: var(--muted, GrayText);
|
|
405
405
|
}
|
|
406
406
|
|
|
407
|
-
[data-grammar-menu-item][data-grammar-tone="
|
|
407
|
+
[data-grammar-menu-item][data-grammar-tone="danger"] .starci-core-icon {
|
|
408
408
|
color: var(--danger, CanvasText);
|
|
409
409
|
}
|
|
410
410
|
|
|
411
|
+
/* Marks reuse Icon's leading box; artwork fits that box without a second size rule. */
|
|
412
|
+
[data-grammar-menu-mark] {
|
|
413
|
+
display: inline-flex;
|
|
414
|
+
align-items: center;
|
|
415
|
+
justify-content: center;
|
|
416
|
+
}
|
|
417
|
+
|
|
418
|
+
/* A leading node (an identity mark) sits in a box at least as big as the 20px a glyph gets (the canon icon rule, `Icon` `leading`), centred. */
|
|
419
|
+
[data-grammar-menu-leading] {
|
|
420
|
+
display: inline-flex;
|
|
421
|
+
flex: none;
|
|
422
|
+
min-inline-size: 1.25rem;
|
|
423
|
+
min-block-size: 1.25rem;
|
|
424
|
+
align-items: center;
|
|
425
|
+
justify-content: center;
|
|
426
|
+
}
|
|
427
|
+
|
|
428
|
+
[data-grammar-menu-mark] > * {
|
|
429
|
+
width: 100%;
|
|
430
|
+
height: 100%;
|
|
431
|
+
max-width: 100%;
|
|
432
|
+
max-height: 100%;
|
|
433
|
+
object-fit: contain;
|
|
434
|
+
}
|
|
435
|
+
|
|
411
436
|
[data-grammar-menu-icon-slot] {
|
|
412
437
|
display: flex;
|
|
413
438
|
block-size: 2rem;
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/* Field-shaped press targets share HeroUI secondary field paint inside a surface. */
|
|
2
|
+
@layer starci-grammar-common {
|
|
3
|
+
.starci-core-pressable-field[data-grammar-variant="secondary"] {
|
|
4
|
+
background: var(--default, var(--surface-secondary, Canvas));
|
|
5
|
+
box-shadow: none;
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
.starci-core-pressable-field[data-grammar-variant="secondary"]:hover:not(:disabled) {
|
|
9
|
+
background: var(--default-hover, var(--default, var(--surface-secondary, Canvas)));
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
.starci-core-pressable-field[data-grammar-variant="secondary"]:focus-visible {
|
|
13
|
+
background: var(--default, var(--surface-secondary, Canvas));
|
|
14
|
+
}
|
|
15
|
+
}
|