@sableui/react 0.0.0-stage → 0.1.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/AGENTS.md +157 -0
- package/CHANGELOG.md +55 -0
- package/CONTRIBUTING.md +221 -0
- package/DESIGN.md +548 -0
- package/LICENSE +21 -0
- package/README.md +194 -2
- package/THIRD_PARTY_NOTICES.md +44 -0
- package/bin/sable-lint.mjs +0 -0
- package/bin/sable.mjs +1223 -0
- package/dist/_contract-CYScOdf5.d.ts +150 -0
- package/dist/chunk-3IWZYBGH.js +398 -0
- package/dist/chunk-5Z5KLOAU.js +167 -0
- package/dist/chunk-7MG4SHYX.js +15 -0
- package/dist/chunk-ADLLDWLW.js +15 -0
- package/dist/chunk-EP55KSYA.js +14 -0
- package/dist/chunk-J23ELQQZ.js +9396 -0
- package/dist/chunk-JI7WJP2L.js +286 -0
- package/dist/chunk-K7RUHUGC.js +14 -0
- package/dist/chunk-KO3NAEBF.js +15 -0
- package/dist/chunk-L4IEIQQA.js +14 -0
- package/dist/chunk-L6Y46C62.js +270 -0
- package/dist/chunk-NEQNXI2G.js +25 -0
- package/dist/chunk-PAOZBZME.js +40 -0
- package/dist/chunk-RWP4XBSK.js +139 -0
- package/dist/chunk-SRQWQUTW.js +63 -0
- package/dist/chunk-YXBOYWBN.js +15 -0
- package/dist/foundations/index.d.ts +88 -0
- package/dist/foundations/index.js +792 -0
- package/dist/icon/index.d.ts +60 -0
- package/dist/icon/index.js +2 -0
- package/dist/icon-sets/brands.d.ts +44 -0
- package/dist/icon-sets/brands.js +147 -0
- package/dist/icon-sets/reicon.d.ts +8 -0
- package/dist/icon-sets/reicon.js +1 -0
- package/dist/index.d.ts +7013 -0
- package/dist/index.js +15462 -0
- package/dist/ios/chrome.css +83 -0
- package/dist/ios/frame.css +5 -0
- package/dist/ios/index.d.ts +70 -0
- package/dist/ios/index.js +313 -0
- package/dist/ios/preview.d.ts +64 -0
- package/dist/ios/preview.js +133 -0
- package/dist/ios/styles.css +77 -0
- package/dist/layout/index.d.ts +280 -0
- package/dist/layout/index.js +2 -0
- package/dist/meta-6dUzgC6M.d.ts +205 -0
- package/dist/patterns/index.d.ts +36 -0
- package/dist/patterns/index.js +934 -0
- package/dist/presets/index.d.ts +37 -0
- package/dist/presets/index.js +117 -0
- package/dist/registry-CyL28OzJ.d.ts +28 -0
- package/dist/styles.css +1893 -0
- package/dist/theme-provider-BWcizhAt.d.ts +105 -0
- package/dist/themes/atelier/atelier.css +213 -0
- package/dist/themes/atelier/index.d.ts +9 -0
- package/dist/themes/atelier/index.js +1 -0
- package/dist/themes/base/base.css +443 -0
- package/dist/themes/base/index.d.ts +9 -0
- package/dist/themes/base/index.js +1 -0
- package/dist/themes/ember/ember.css +270 -0
- package/dist/themes/ember/index.d.ts +9 -0
- package/dist/themes/ember/index.js +1 -0
- package/dist/themes/index.d.ts +10 -0
- package/dist/themes/index.js +8 -0
- package/dist/themes/meridian/index.d.ts +9 -0
- package/dist/themes/meridian/index.js +1 -0
- package/dist/themes/meridian/meridian.css +255 -0
- package/dist/themes/poppy/index.d.ts +6 -0
- package/dist/themes/poppy/index.js +1 -0
- package/dist/themes/poppy/poppy.css +174 -0
- package/dist/themes/prism/index.d.ts +6 -0
- package/dist/themes/prism/index.js +1 -0
- package/dist/themes/prism/prism.css +183 -0
- package/dist/themes/verdant/index.d.ts +9 -0
- package/dist/themes/verdant/index.js +1 -0
- package/dist/themes/verdant/verdant.css +210 -0
- package/dist/themes.css +30 -0
- package/llms/components/accordion.md +93 -0
- package/llms/components/app-header.md +70 -0
- package/llms/components/app-shell.md +117 -0
- package/llms/components/attachment.md +105 -0
- package/llms/components/avatar.md +141 -0
- package/llms/components/badge.md +117 -0
- package/llms/components/banner.md +119 -0
- package/llms/components/bottom-nav.md +80 -0
- package/llms/components/breadcrumbs.md +75 -0
- package/llms/components/browser.md +67 -0
- package/llms/components/bubble.md +119 -0
- package/llms/components/button.md +199 -0
- package/llms/components/calendar.md +75 -0
- package/llms/components/card.md +135 -0
- package/llms/components/carousel.md +87 -0
- package/llms/components/chat-composer.md +126 -0
- package/llms/components/checkbox-card.md +98 -0
- package/llms/components/checkbox.md +90 -0
- package/llms/components/code-block.md +81 -0
- package/llms/components/code.md +62 -0
- package/llms/components/collapsible.md +83 -0
- package/llms/components/color-field.md +100 -0
- package/llms/components/combobox.md +119 -0
- package/llms/components/command-menu.md +158 -0
- package/llms/components/context-card.md +73 -0
- package/llms/components/context-menu.md +79 -0
- package/llms/components/data-table.md +99 -0
- package/llms/components/date-picker.md +97 -0
- package/llms/components/description.md +96 -0
- package/llms/components/destructive-action-modal.md +76 -0
- package/llms/components/dialog.md +127 -0
- package/llms/components/dots-menu.md +64 -0
- package/llms/components/empty-state.md +113 -0
- package/llms/components/entity.md +67 -0
- package/llms/components/feedback.md +63 -0
- package/llms/components/fieldset.md +106 -0
- package/llms/components/file-dropzone.md +103 -0
- package/llms/components/form.md +111 -0
- package/llms/components/gliding-highlight.md +84 -0
- package/llms/components/inline-status.md +62 -0
- package/llms/components/input.md +127 -0
- package/llms/components/kbd.md +57 -0
- package/llms/components/label.md +73 -0
- package/llms/components/marker.md +99 -0
- package/llms/components/marquee.md +66 -0
- package/llms/components/menu.md +122 -0
- package/llms/components/message-scroller.md +110 -0
- package/llms/components/message.md +114 -0
- package/llms/components/meter.md +124 -0
- package/llms/components/middle-truncate.md +62 -0
- package/llms/components/note.md +81 -0
- package/llms/components/notification-badge.md +67 -0
- package/llms/components/number-field.md +70 -0
- package/llms/components/otp-field.md +101 -0
- package/llms/components/page-header.md +76 -0
- package/llms/components/page-loader.md +67 -0
- package/llms/components/pagination.md +92 -0
- package/llms/components/panel-group.md +120 -0
- package/llms/components/panel-section.md +122 -0
- package/llms/components/phone.md +70 -0
- package/llms/components/pill.md +73 -0
- package/llms/components/popover.md +79 -0
- package/llms/components/progress.md +98 -0
- package/llms/components/radio-card.md +90 -0
- package/llms/components/radio.md +86 -0
- package/llms/components/relative-time.md +60 -0
- package/llms/components/scroller.md +88 -0
- package/llms/components/scrub-field.md +97 -0
- package/llms/components/search-input.md +84 -0
- package/llms/components/select.md +106 -0
- package/llms/components/separator.md +70 -0
- package/llms/components/settings-menu.md +139 -0
- package/llms/components/sheet.md +97 -0
- package/llms/components/show-more.md +59 -0
- package/llms/components/side-nav.md +106 -0
- package/llms/components/sidebar.md +87 -0
- package/llms/components/skeleton.md +62 -0
- package/llms/components/slider.md +114 -0
- package/llms/components/spinner.md +87 -0
- package/llms/components/status-bar.md +91 -0
- package/llms/components/status-dot.md +70 -0
- package/llms/components/stepper.md +125 -0
- package/llms/components/streaming-text.md +86 -0
- package/llms/components/switch.md +87 -0
- package/llms/components/table.md +112 -0
- package/llms/components/tabs.md +84 -0
- package/llms/components/textarea.md +88 -0
- package/llms/components/theme-selector.md +95 -0
- package/llms/components/theme-switcher.md +71 -0
- package/llms/components/thinking-orb.md +91 -0
- package/llms/components/toast.md +130 -0
- package/llms/components/toggle.md +95 -0
- package/llms/components/toolbar.md +168 -0
- package/llms/components/tooltip.md +93 -0
- package/llms/components/tree.md +227 -0
- package/llms/components/video.md +66 -0
- package/llms/components/workspace.md +138 -0
- package/llms/foundations/accessibility.md +60 -0
- package/llms/foundations/motion.md +96 -0
- package/llms/foundations/scrolling.md +41 -0
- package/llms/foundations/shape.md +73 -0
- package/llms/foundations/spacing.md +82 -0
- package/llms/foundations/surfaces.md +69 -0
- package/llms/foundations/typography.md +120 -0
- package/llms/layout/aspect-box.md +37 -0
- package/llms/layout/bleed.md +31 -0
- package/llms/layout/center.md +37 -0
- package/llms/layout/columns.md +51 -0
- package/llms/layout/grid-overlay.md +40 -0
- package/llms/layout/grid.md +47 -0
- package/llms/layout/page.md +39 -0
- package/llms/layout/region.md +49 -0
- package/llms/layout/row.md +40 -0
- package/llms/layout/safe-area.md +37 -0
- package/llms/layout/spacer.md +39 -0
- package/llms/layout/split-pane.md +41 -0
- package/llms/layout/stack.md +39 -0
- package/llms/patterns/auth-page.md +68 -0
- package/llms/patterns/brand-landing-page.md +423 -0
- package/llms/patterns/chat-panel.md +210 -0
- package/llms/patterns/dashboard-page.md +170 -0
- package/llms/patterns/feed-page.md +103 -0
- package/llms/patterns/landing-page.md +122 -0
- package/llms/patterns/list-detail-page.md +100 -0
- package/llms/patterns/settings-page.md +121 -0
- package/llms/reference/token-intent.md +324 -0
- package/llms/reference/utilities.md +129 -0
- package/llms/tasks/adding-a-component.md +113 -0
- package/llms/tasks/auditing-an-app.md +216 -0
- package/llms/tasks/building-a-page.md +109 -0
- package/llms/tasks/building-a-tool.md +141 -0
- package/llms/tasks/building-an-ios-prototype.md +105 -0
- package/llms/tasks/building-with-sable.md +73 -0
- package/llms/tasks/choosing-a-component.md +109 -0
- package/llms/tasks/converting-an-app.md +139 -0
- package/llms/tasks/creating-a-preset.md +43 -0
- package/llms/tasks/creating-design-assets.md +96 -0
- package/llms/tasks/designing-a-surface.md +115 -0
- package/llms/tasks/developing-taste.md +157 -0
- package/llms/tasks/getting-started.md +63 -0
- package/llms/tasks/learning-from-builds.md +74 -0
- package/llms/tasks/reviewing-a-design.md +114 -0
- package/llms/tasks/reviewing-a-journey.md +84 -0
- package/llms/tasks/reviewing-motion.md +92 -0
- package/llms/tasks/using-presets.md +87 -0
- package/llms/taste/asset-plan.template.json +53 -0
- package/llms/taste/composition-follows-content.md +20 -0
- package/llms/taste/hierarchy-is-a-relationship.md +22 -0
- package/llms/taste/lessons.json +57 -0
- package/llms/taste/motion-needs-a-reason.md +21 -0
- package/llms/taste/surface-states-travel-together.md +20 -0
- package/llms-full.txt +14906 -0
- package/llms.txt +405 -0
- package/package.json +185 -3
- package/registry.json +18812 -0
- package/scripts/agent-setup.mjs +54 -0
- package/scripts/design-atlas.mjs +221 -0
- package/scripts/design-directions.mjs +123 -0
- package/scripts/design-memory.mjs +166 -0
- package/scripts/design-preferences.mjs +227 -0
- package/scripts/design-presets.mjs +48 -0
- package/scripts/grade.mjs +509 -0
- package/scripts/lint-rules.mjs +1065 -0
- package/scripts/review-evidence.mjs +120 -0
- package/scripts/skeleton.mjs +248 -0
- package/skills/entry/SKILL.md +34 -0
- package/skills/sable/SKILL.md +187 -0
- package/skills/sable/agents/openai.yaml +6 -0
- package/targets/ios.json +982 -0
- package/third-party/reicon-LICENSE.txt +20 -0
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
# Task: building an iOS app prototype
|
|
2
|
+
|
|
3
|
+
An iOS prototype is a clickable iPhone app that runs in the browser, for demos,
|
|
4
|
+
presentations and user testing. It is not a native app and not for production.
|
|
5
|
+
|
|
6
|
+
## The rule: Sable first
|
|
7
|
+
|
|
8
|
+
**Every screen is built from Sable components, in the chosen preset.** Rows,
|
|
9
|
+
lists, inputs, switches, sliders, search, selects, buttons, forms, toasts,
|
|
10
|
+
empty states, cards and all content are Sable — exactly as on the web.
|
|
11
|
+
|
|
12
|
+
The Sable iOS adapter (`@sableui/react/ios`) supplies only the iOS **system
|
|
13
|
+
chrome** Sable has no equivalent for:
|
|
14
|
+
|
|
15
|
+
| Chrome | Adapter |
|
|
16
|
+
| --- | --- |
|
|
17
|
+
| App frame, status bar, safe areas | `NativeApp` |
|
|
18
|
+
| A screen | `IosPage` |
|
|
19
|
+
| Navigation bar: title, large title, back, bar actions | `IosNavbar`, `IosBackLink`, `IosIconButton` |
|
|
20
|
+
| Tab bar | `IosTabbar`, `IosTab` |
|
|
21
|
+
| Sheet presentation (detents, drag, Escape) | `IosSheet` (its content is Sable) |
|
|
22
|
+
| Push and pop between screens | `NavigationStack`, `useNavigation` |
|
|
23
|
+
|
|
24
|
+
**Never import from `konsta`.** Konsta is the adapter's optional peer, not a
|
|
25
|
+
component library for the app; `sable-lint` flags a direct import
|
|
26
|
+
(`native-ui-import`). If a screen needs something Sable does not have, report
|
|
27
|
+
it as a library gap; do not reach for a native control or hand-build one.
|
|
28
|
+
|
|
29
|
+
`sable target ios --component <Name> --json` gives the decision for any
|
|
30
|
+
component: `preserve` (use the Sable component — almost everything),
|
|
31
|
+
`replace` (an adapter chrome component), `contextual` (Button and PageHeader:
|
|
32
|
+
the adapter only in the navigation bar, Sable everywhere else), `recompose`
|
|
33
|
+
(desktop patterns rebuilt from Sable parts for a phone, e.g. a table becomes
|
|
34
|
+
Entity rows in a Card) or `redesign` (desktop workspace tools).
|
|
35
|
+
|
|
36
|
+
## How iOS screens look in Sable
|
|
37
|
+
|
|
38
|
+
- **Grouped settings:** a section heading (`text-title-sm text-muted-foreground`)
|
|
39
|
+
over a `Card` with `CardList` and `Entity` rows; a `Switch`, value text or
|
|
40
|
+
chevron in each row's `action`.
|
|
41
|
+
- **Forms:** `Form` with `Field` around `Input`, `Textarea`, `Select`, `Slider`.
|
|
42
|
+
- **Lists that drill down:** interactive `Entity` rows in a `Card` that call
|
|
43
|
+
`useNavigation().push(id, <NextScreen />)`.
|
|
44
|
+
- **Search:** `SearchInput` on the page margin.
|
|
45
|
+
- **Feedback:** `toast()` inside `ToastProvider`; `EmptyState` for no results.
|
|
46
|
+
- Align content to the native page margin with `px-(--ios-inset)` so it lines
|
|
47
|
+
up with large titles. The adapter spaces a screen's first content below its
|
|
48
|
+
title.
|
|
49
|
+
|
|
50
|
+
## Setup
|
|
51
|
+
|
|
52
|
+
1. Install `konsta` in the consuming project only when an iOS prototype is
|
|
53
|
+
requested (`pnpm add konsta`). It is the adapter's optional peer.
|
|
54
|
+
2. Give the prototype its own HTML entry, separate from the web app, with one
|
|
55
|
+
stylesheet:
|
|
56
|
+
|
|
57
|
+
```css
|
|
58
|
+
@import "tailwindcss";
|
|
59
|
+
@import "@sableui/react/styles.css";
|
|
60
|
+
@import "@sableui/react/theme/verdant.css"; /* the preset, or themes.css */
|
|
61
|
+
@import "@sableui/react/ios/styles.css";
|
|
62
|
+
@source "../node_modules/@sableui/react/dist";
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
Set the preset with `data-sable-theme` on `<html>`. The adapter maps the
|
|
66
|
+
preset onto the chrome (background, ink, accent, fonts), so chrome and
|
|
67
|
+
content follow the same preset and light or dark mode. It pins Tailwind's
|
|
68
|
+
spacing and radii on the chrome only, so a preset's density and corners
|
|
69
|
+
reach your Sable content and the chrome keeps iOS geometry.
|
|
70
|
+
3. Compose: `NativeApp` at the root (`mode` sets light or dark, also on
|
|
71
|
+
`<html>` so portaled Sable popups follow); one `NavigationStack` per tab,
|
|
72
|
+
each holding an `IosPage` with an `IosNavbar` and Sable content; an
|
|
73
|
+
`IosTabbar` of `IosTab`s with Sable `Icon`s; `IosSheet` around Sable forms.
|
|
74
|
+
Keep the app's `IconProvider`.
|
|
75
|
+
4. Frame it on a host page with `PhoneFrame` from `@sableui/react/ios/preview`
|
|
76
|
+
and `@sableui/react/ios/frame.css` (Sable's Phone around an iframe at
|
|
77
|
+
402×874, scaled as a whole device). To follow a host's live look, call
|
|
78
|
+
`useIosPreviewBridge(frameRef, { mode })` on the host and
|
|
79
|
+
`useIosPreviewTheme()` in the prototype.
|
|
80
|
+
5. Verify light and dark, keyboard focus, reduced motion, back and edge swipe,
|
|
81
|
+
independent tabs, sheet drag, Escape and cancel, form validation and save,
|
|
82
|
+
and empty-state recovery. Run `sable-lint` over the prototype: it is a Sable
|
|
83
|
+
app and the same rules apply.
|
|
84
|
+
|
|
85
|
+
## Geometry hooks
|
|
86
|
+
|
|
87
|
+
`--ios-inset` (16px page margin), `--ios-safe-top` (62px), `--ios-header-height`,
|
|
88
|
+
`--ios-tabbar-bottom`, `--ios-content-bottom` and `--ios-motion`, for the
|
|
89
|
+
standard 402×874 phone. Reduced transparency removes the glass blur.
|
|
90
|
+
|
|
91
|
+
## Engineering handoff
|
|
92
|
+
|
|
93
|
+
The prototype's colors, fonts, radii and spacing are the preset's tokens.
|
|
94
|
+
Engineers take styles from the preset export (Customize → Export, or
|
|
95
|
+
`sable preset`), not code from the prototype.
|
|
96
|
+
|
|
97
|
+
Platform: iOS 27 on a 6.3" iPhone Pro. Chrome by Konsta UI 5.5 (MIT) through
|
|
98
|
+
the adapter.
|
|
99
|
+
|
|
100
|
+
```sh
|
|
101
|
+
sable target ios --json
|
|
102
|
+
sable target ios --component Switch --json # preserve: Sable Switch
|
|
103
|
+
sable target ios --component BottomNav --json # replace: IosTabbar + IosTab
|
|
104
|
+
sable target ios --component Button --json # contextual: Sable except in the nav bar
|
|
105
|
+
```
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
# Task: build an idea with Sable
|
|
2
|
+
|
|
3
|
+
Start with the product the user wants: a portfolio, shop, booking service, app,
|
|
4
|
+
or another concrete task. A preset supplies visual decisions; the brief supplies
|
|
5
|
+
content, information architecture, and behavior. Inspiration is optional.
|
|
6
|
+
|
|
7
|
+
## One workflow
|
|
8
|
+
|
|
9
|
+
1. Install and wire Sable using [getting-started.md](./getting-started.md).
|
|
10
|
+
Run `pnpm exec sable setup --agent` to connect the package-owned skill.
|
|
11
|
+
2. Choose a bundled preset, customize one in Canvas, or use `inspire` to develop
|
|
12
|
+
an original preset from references. See [creating-a-preset.md](./creating-a-preset.md).
|
|
13
|
+
Starting directly from an idea is equally valid.
|
|
14
|
+
3. Put a downloaded preset in `.sable/presets/<id>.json` in the target project.
|
|
15
|
+
Ask the agent to build your idea using that exact file. Read
|
|
16
|
+
[using-presets.md](./using-presets.md) for validation, CSS, and fonts.
|
|
17
|
+
4. Build from Sable's installed components, patterns, utilities, and tokens.
|
|
18
|
+
Inspect and operate the real result, then repair observed problems.
|
|
19
|
+
|
|
20
|
+
Examples in Codex:
|
|
21
|
+
|
|
22
|
+
```text
|
|
23
|
+
$sable build a portfolio for a furniture designer
|
|
24
|
+
$sable inspire these references into a warm, compact shop preset
|
|
25
|
+
$sable build a ceramics shop using .sable/presets/clay.json
|
|
26
|
+
$sable audit our checkout and report prioritized findings
|
|
27
|
+
$sable polish our checkout and repair the reported issues
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
Claude Code and Cursor use `/sable` with the same modes. `create` is a `build`
|
|
31
|
+
alias. These are agent instructions; the shell CLI serves catalog resources and
|
|
32
|
+
validates records. Other agents can read the package's `skills/sable/SKILL.md`.
|
|
33
|
+
|
|
34
|
+
`inspire` produces a preset and component preview. `audit` evaluates an existing
|
|
35
|
+
interface and reports by default. A reference URL does not request a cloned site.
|
|
36
|
+
Develop alternative rendered directions when requested or when a consequential
|
|
37
|
+
choice needs comparison. A three-design comparison is not a mandatory build step.
|
|
38
|
+
|
|
39
|
+
## Required library use
|
|
40
|
+
|
|
41
|
+
Read the installed package's `AGENTS.md` and `registry.json` consume block. Search
|
|
42
|
+
for each recognizable element with `sable search "<need>" --json`; inspect the
|
|
43
|
+
matching `component <Name> --props` and `--examples` before implementation.
|
|
44
|
+
Use Sable's composed anatomy and supported variants. A hand-built button, field,
|
|
45
|
+
menu, dialog, table, or card that imitates a shipped component violates the contract.
|
|
46
|
+
|
|
47
|
+
Resolve styling through component variants, documented utilities/recipes, then
|
|
48
|
+
semantic tokens. App-owned CSS handles uncovered content and composition; it
|
|
49
|
+
does not replace component appearance or behavior. Inspect a relevant pattern's
|
|
50
|
+
skeleton; compose an original layout if its structure does not fit the brief.
|
|
51
|
+
Read [choosing-a-component.md](./choosing-a-component.md) and
|
|
52
|
+
[designing-a-surface.md](./designing-a-surface.md).
|
|
53
|
+
|
|
54
|
+
For a real library gap, record the unsuccessful search and missing capability.
|
|
55
|
+
In the Sable repository, add the capability to the package under `CONTRIBUTING.md`
|
|
56
|
+
before consuming it. In another project, report the gap and preserve the package;
|
|
57
|
+
implement genuinely app-specific structure without creating parallel controls.
|
|
58
|
+
|
|
59
|
+
## Finish the working product
|
|
60
|
+
|
|
61
|
+
Run `sable doctor`, `sable lint <source paths> --json`, and the project's build/type
|
|
62
|
+
checks. Inspect desktop/mobile layouts, operate the primary task and recovery
|
|
63
|
+
path, and check keyboard focus, fonts, reduced motion, and meaningful states.
|
|
64
|
+
Use [reviewing-a-design.md](./reviewing-a-design.md),
|
|
65
|
+
[reviewing-a-journey.md](./reviewing-a-journey.md), and
|
|
66
|
+
[reviewing-motion.md](./reviewing-motion.md) as applicable.
|
|
67
|
+
|
|
68
|
+
Passing static checks does not establish visual quality. Review composition,
|
|
69
|
+
hierarchy, rhythm, imagery, and brief fit in the actual product. Deliver the
|
|
70
|
+
working result, preset/revision used, checks, and any material limitations.
|
|
71
|
+
Include a reuse inventory: imported Sable components and parts, retrieved token
|
|
72
|
+
groups/utilities, and the reason for each app-owned UI/CSS addition. Inspect source
|
|
73
|
+
ownership; valid classes on a hand-built control still violate the contract.
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
# Task: choosing between similar components
|
|
2
|
+
|
|
3
|
+
Sable has several components that look alike and do different jobs. Choosing
|
|
4
|
+
the wrong one usually still renders, so nothing fails. It shows up later as
|
|
5
|
+
a focus trap where none belonged, a menu that cannot hold a form, or a status
|
|
6
|
+
that a screen reader never announces. Use the tables below before you compose.
|
|
7
|
+
For each component, `npx sable component <Name> --detail full` has the full
|
|
8
|
+
contract.
|
|
9
|
+
|
|
10
|
+
The question to ask is always **what the user is doing**, not what it looks like.
|
|
11
|
+
|
|
12
|
+
## Surfaces that open over the page
|
|
13
|
+
|
|
14
|
+
| The user is… | Use | Not |
|
|
15
|
+
|---|---|---|
|
|
16
|
+
| Making a focused decision or filling a short form, and the page should wait | `Dialog` | `Sheet` (too much room), `Popover` (no focus trap) |
|
|
17
|
+
| Confirming something irreversible | `DestructiveActionModal` | a `Dialog` you style red: it already handles the async confirm, the spinner and the verb-first labels |
|
|
18
|
+
| Working through a long or secondary task beside the page context (filters, details, an edit form) | `Sheet` | `Dialog` (cramps long content) |
|
|
19
|
+
| Viewing media full-bleed | `Dialog` with `DialogContent size="fullscreen"` | `Dialog` at its default size (500px cap) |
|
|
20
|
+
| Adjusting a few controls anchored to a trigger, without leaving the flow | `Popover` | `Menu` (cannot hold inputs) |
|
|
21
|
+
| Reading a rich preview of a link or a name on hover or focus | `ContextCard` | `Tooltip` (plain text only), `Popover` (needs a click) |
|
|
22
|
+
| Reading a short label for an icon or control | `Tooltip` | anything that holds interactive content: tooltips are not reachable by touch |
|
|
23
|
+
| Jumping anywhere or running a command by name | `CommandMenu` | `Combobox` (one field's value, not app-wide actions) |
|
|
24
|
+
| Sending a quick rating and message | `Feedback` | a hand-built `Popover` + `Textarea` |
|
|
25
|
+
|
|
26
|
+
## Lists of actions
|
|
27
|
+
|
|
28
|
+
| The user is… | Use | Not |
|
|
29
|
+
|---|---|---|
|
|
30
|
+
| Choosing one action from a list under a button | `Menu` | `Select` (that picks a *value*) |
|
|
31
|
+
| Reaching secondary actions for a row or card | `DotsMenu` | a bare `Menu` with a hand-made icon trigger |
|
|
32
|
+
| Acting on the thing under the pointer (right-click or long press) | `ContextMenu`, always mirrored in a visible `DotsMenu` | `ContextMenu` alone: it is undiscoverable |
|
|
33
|
+
| Changing a few settings that each show their current value | `SettingsMenu` | a `Menu` of toggles that hides each current state |
|
|
34
|
+
| Running a main action that has close variations | a `Button` beside an icon `Button` that opens a `Menu` | a hand-fused split control |
|
|
35
|
+
|
|
36
|
+
## Picking a value
|
|
37
|
+
|
|
38
|
+
| The user is… | Use | Not |
|
|
39
|
+
|---|---|---|
|
|
40
|
+
| Picking one of 2–5 visible options | `Radio` (`RadioGroup`) | `Select`: hiding five options behind a click costs a click |
|
|
41
|
+
| Picking one option that needs a title and description | `RadioCard` (in a `RadioGroup`) | `Radio` with hand-styled cards |
|
|
42
|
+
| Picking several options that need a title and description | `CheckboxCard` | `Checkbox` with hand-styled cards |
|
|
43
|
+
| Picking one of many known options | `Select` | `Combobox`, unless the list is long enough to need typing |
|
|
44
|
+
| Picking one of a long or searchable list | `Combobox` | `Select` (no filtering) |
|
|
45
|
+
| Picking several from a long list | `Combobox multiple` with `ComboboxChips` | a column of `Checkbox`es past about eight items |
|
|
46
|
+
| Turning a setting on or off with an immediate effect | `Switch` | `Checkbox` (that is a form value applied on submit) |
|
|
47
|
+
| Agreeing to something, or ticking a form value submitted later | `Checkbox` | `Switch` |
|
|
48
|
+
| Switching a view mode, or pressing a formatting toggle | `Toggle` / `ToggleGroup` | `Tabs` (those swap content panels) |
|
|
49
|
+
| Entering a one-time code | `OtpField` | `Input type="number"` |
|
|
50
|
+
| Entering a number, nudging with keys or steppers | `NumberField` | `Input type="number"` |
|
|
51
|
+
| Scrubbing a number by dragging, in tool chrome | `ScrubField` | `Slider` (page register) |
|
|
52
|
+
| Choosing a value along a range | `Slider` | `NumberField` when the exact value matters more than position |
|
|
53
|
+
| Adding files | `FileDropzone`, with accepted files listed as `Attachment` rows | a bare `<input type="file">` |
|
|
54
|
+
|
|
55
|
+
## Showing more or less
|
|
56
|
+
|
|
57
|
+
| The user is… | Use | Not |
|
|
58
|
+
|---|---|---|
|
|
59
|
+
| Switching between peer views of one object | `Tabs` | `Accordion` (stacked, not peer) |
|
|
60
|
+
| Scanning several sections and opening one or more | `Accordion` | a column of `Collapsible`s you coordinate by hand |
|
|
61
|
+
| Opening one optional block | `Collapsible` | `Accordion` with one item |
|
|
62
|
+
| Reading a long block that starts clipped | `ShowMore` | a fixed-height box with `overflow-auto` |
|
|
63
|
+
|
|
64
|
+
## Status, progress and feedback
|
|
65
|
+
|
|
66
|
+
| What you are communicating | Use | Not |
|
|
67
|
+
|---|---|---|
|
|
68
|
+
| Work happening, with a known percentage | `Progress` | `Meter` (not task progress) |
|
|
69
|
+
| How full something is within a known range (storage, quota, strength) | `Meter`, or `Meter shape="ring"` in a stat card | `Progress` |
|
|
70
|
+
| Where the user is in a multi-step flow | `Stepper` | `Progress` (loses which step) |
|
|
71
|
+
| Content loading in place, with a known shape | `Skeleton` | `Spinner` (makes the layout jump when content lands) |
|
|
72
|
+
| A short wait with no known shape | `Spinner` | `Skeleton` |
|
|
73
|
+
| Someone typing, or a reply composing | `Spinner appearance="dots"` / `StreamingText` | the ring `Spinner` |
|
|
74
|
+
| The whole app booting | `PageLoader` | a centred `Spinner` |
|
|
75
|
+
| The result of an action, transient | `Toast` | `Banner` (persistent) |
|
|
76
|
+
| A one-line status beside the thing it describes | `InlineStatus` | `Toast` (detached from the cause) |
|
|
77
|
+
| Guidance or a caveat inside content | `Note` | `Banner` |
|
|
78
|
+
| A page-wide announcement | `Banner` | `Note` |
|
|
79
|
+
| Which scope or environment the user is in (preview, staging) | `Banner` with a `label` chip | a hand-built strip |
|
|
80
|
+
| Nothing here yet, with a next step | `EmptyState` | an empty `Table` |
|
|
81
|
+
| A failure with a recovery action, in a region | `EmptyState tone="destructive"` (`surface="card"` among cards, `size="lg"` for a page) | `Note tone="destructive"` |
|
|
82
|
+
| A state label (live, failed, draft) | `Badge appearance="plain" tone=… dot` (dot plus label) | `StatusDot` alone: it is decorative and silent |
|
|
83
|
+
| Metadata or a category | `Badge`; `Pill` when it can be selected | `Badge appearance="plain" dot` (that is a status) |
|
|
84
|
+
|
|
85
|
+
## Tables and trees
|
|
86
|
+
|
|
87
|
+
| The data is… | Use | Not |
|
|
88
|
+
|---|---|---|
|
|
89
|
+
| Static rows you lay out by hand | `Table` | `DataTable` |
|
|
90
|
+
| Rows from an array that need sorting or selection | `DataTable` | `Table` plus hand-written sort state |
|
|
91
|
+
| Files, folders, layers or an outline | `Tree` (`TreeRow icon`) | a nested list built by hand |
|
|
92
|
+
|
|
93
|
+
## Navigation
|
|
94
|
+
|
|
95
|
+
| The user is… | Use | Not |
|
|
96
|
+
|---|---|---|
|
|
97
|
+
| Moving between the app's top-level areas on desktop | `Sidebar` inside `AppShell` | `SideNav` |
|
|
98
|
+
| Moving between sub-pages of a section (settings, docs) | `SideNav` | `Sidebar` (that is app chrome) |
|
|
99
|
+
| Moving between top-level areas on mobile | `BottomNav` | a hamburger-only `Sheet` |
|
|
100
|
+
| Paging through results with known counts | `Pagination` | `Button variant="secondary" loading` |
|
|
101
|
+
| Continuing a feed | `<Button variant="secondary" loading loadingLabel="Loading…">` | `Pagination` |
|
|
102
|
+
|
|
103
|
+
## When nothing fits
|
|
104
|
+
|
|
105
|
+
Compose from layout primitives (`Stack`, `Row`, `Grid`, `Region`) and the
|
|
106
|
+
closest component. Do not restyle a component into a different one. If you
|
|
107
|
+
find yourself fighting a component's role, you are holding the wrong one.
|
|
108
|
+
Retrieve its `usage.bestPractices`, which name the neighbouring component to
|
|
109
|
+
use instead.
|
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
# Task: converting an existing app to Sable
|
|
2
|
+
|
|
3
|
+
Migrating a React app that has its own styles. The order below front-loads the
|
|
4
|
+
decisions that invalidate later work if you get them wrong.
|
|
5
|
+
|
|
6
|
+
## 0. Classify every surface first
|
|
7
|
+
|
|
8
|
+
Before touching a component, **list the routes and assign a register to each** —
|
|
9
|
+
see fork 1 in `llms.txt`. Do not decide once for the app. Every conversion so far
|
|
10
|
+
has applied a single register throughout and been wrong somewhere: the tell is a
|
|
11
|
+
route with arrow-key or bracket-key bindings (triage), or one that never scrolls,
|
|
12
|
+
sitting in a page-register app.
|
|
13
|
+
|
|
14
|
+
Converting a tool with page-register instincts produces something that passes
|
|
15
|
+
every lint and still looks wrong. The measurable tells that a surface was built
|
|
16
|
+
with page instincts but is actually a tool:
|
|
17
|
+
|
|
18
|
+
| tell | page register | you are probably a tool |
|
|
19
|
+
|---|---|---|
|
|
20
|
+
| dominant control height | 40–44px | should be 32 (24 in dense chrome) |
|
|
21
|
+
| dominant radius | 8–16px, some pills | ~5px, **no pills in chrome** |
|
|
22
|
+
| dominant UI type | 14px / 600 | 10–12px at 450–550 |
|
|
23
|
+
| panel grouping | `Card` | `PanelSection` + hairlines |
|
|
24
|
+
|
|
25
|
+
Measure these in the running app before deciding — `getComputedStyle` over every
|
|
26
|
+
button and text node takes a minute and settles the argument.
|
|
27
|
+
|
|
28
|
+
## 1. Wire the package
|
|
29
|
+
|
|
30
|
+
Follow **Consuming** in `llms.txt`: `.npmrc`, `pnpm add`, React dedupe, the CSS
|
|
31
|
+
import order, `@source`, fonts, providers. React dedupe is the one that fails
|
|
32
|
+
loudest and least clearly ("invalid hook call").
|
|
33
|
+
|
|
34
|
+
## 2. Delete your own tokens
|
|
35
|
+
|
|
36
|
+
The most reliable signal that a conversion is real: the app's own CSS variable
|
|
37
|
+
block shrinks to almost nothing. Anything that remains should be *domain* colour,
|
|
38
|
+
not UI colour.
|
|
39
|
+
|
|
40
|
+
Grep for orphaned references afterwards. A CSS variable that no longer exists
|
|
41
|
+
resolves to nothing and the declaration is dropped — **silently**. A checkerboard
|
|
42
|
+
built on two renamed variables rendered as blank for four releases before anyone
|
|
43
|
+
noticed.
|
|
44
|
+
|
|
45
|
+
## 3. Replace the shell, then the chrome, then the leaves
|
|
46
|
+
|
|
47
|
+
Shell first (`AppShell` or `Workspace`), because it establishes the density scope
|
|
48
|
+
and the landmarks. Then panels/headers. Leaf components last — they are the
|
|
49
|
+
easiest and the least consequential.
|
|
50
|
+
|
|
51
|
+
Expect the shell swap to delete more code than it adds. If it does not, you are
|
|
52
|
+
probably keeping a layout that Sable would have given you.
|
|
53
|
+
|
|
54
|
+
## 4. Icons
|
|
55
|
+
|
|
56
|
+
**Reicon works by default.** No icon provider is needed. To use another icon
|
|
57
|
+
package, map Sable's semantic names in an app-owned IconSet and mount it through
|
|
58
|
+
IconProvider. To add domain-specific names while retaining Reicon:
|
|
59
|
+
|
|
60
|
+
```tsx
|
|
61
|
+
import { IconProvider, composeIconSets, reiconIconSet } from "@sableui/react"
|
|
62
|
+
<IconProvider set={composeIconSets(appExtras, reiconIconSet)} />
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
Keep only the names Sable's contract does not carry and let everything else fall
|
|
66
|
+
through. First set to return non-null wins.
|
|
67
|
+
|
|
68
|
+
**A set must return `null` to fall through — not a placeholder.** This is the trap
|
|
69
|
+
both hand-copied adapters fell into, and it is silent in the worst way: ending
|
|
70
|
+
your renderer with `{node ?? <circle cx="12" cy="12" r="9" />}` means an unknown
|
|
71
|
+
name draws a grey dot, so a typo, an off-contract name and a real icon all look
|
|
72
|
+
like something rendered. Return `null` and the next set gets a turn; miss every
|
|
73
|
+
set and you see nothing, which is at least visibly wrong.
|
|
74
|
+
|
|
75
|
+
Verify the adapter rather than trusting it — nothing in Sable can see it, and
|
|
76
|
+
`sable-lint` does not check it:
|
|
77
|
+
|
|
78
|
+
```tsx
|
|
79
|
+
// Every contract name resolves, and an unknown one returns null.
|
|
80
|
+
const names = registry.iconContract.names // registry.json
|
|
81
|
+
names.filter((n) => !mySet.render({ name: n })) // → must be []
|
|
82
|
+
mySet.render({ name: "definitely-not-an-icon" }) // → must be null
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
Do not hand-copy a name→component mapping. Every app that did ended up with a
|
|
86
|
+
drifted copy.
|
|
87
|
+
|
|
88
|
+
## 5. Verify by measuring, not by looking
|
|
89
|
+
|
|
90
|
+
```bash
|
|
91
|
+
npx sable-lint # lints ./src; pass dirs to change (npx sable-lint app components)
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
`sable-lint` ships with the package and reads the token vocabulary from the
|
|
95
|
+
`registry.json` of the version you installed, so it checks your app against your
|
|
96
|
+
exact tokens. It exits 1 on any violation. `--rules` explains each one; `--ignore
|
|
97
|
+
'<glob>'` skips a data/fixture layer, where a hex value is content rather than a
|
|
98
|
+
style. A rare, genuine exception takes an auditable
|
|
99
|
+
`// design-lint-disable-next-line <ruleId> -- <reason>`.
|
|
100
|
+
|
|
101
|
+
Then your own `tsc` and tests. (`pnpm design-lint && pnpm build:registry &&
|
|
102
|
+
pnpm test:run` are the *in-repo* gates — they only run inside the Sable repo.)
|
|
103
|
+
|
|
104
|
+
Then in the browser, check the things a screenshot cannot show:
|
|
105
|
+
|
|
106
|
+
- every left edge in a hierarchy panel resolves to the rail or an exact multiple
|
|
107
|
+
- control heights match the ladder for the register you chose
|
|
108
|
+
- `--panel`, `--canvas` etc. resolve to real values, not empty
|
|
109
|
+
- **the fonts actually loaded.** `--font-*` resolving is not evidence: the token
|
|
110
|
+
names a family, and if the host never fetched it the browser silently falls back
|
|
111
|
+
to `system-ui` while every token check passes. One app rendered in the wrong
|
|
112
|
+
typeface for its entire life this way, and the documented checks all passed on
|
|
113
|
+
it. The theme names the family; **the host loads it.** Ask the font system, not
|
|
114
|
+
the cascade:
|
|
115
|
+
|
|
116
|
+
```js
|
|
117
|
+
// In the browser console. Both must be true.
|
|
118
|
+
document.fonts.check("1em Figtree") // the theme's sans
|
|
119
|
+
document.fonts.check("1em DM Mono") // the theme's mono
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
If either is false, add the `@font-face`/link the theme expects — see the theme's
|
|
123
|
+
entry in `registry.json → themes` for the families it assumes.
|
|
124
|
+
- a converted colour renders the same bytes as before — compare notations through
|
|
125
|
+
one canvas readback, never against a constant, because the readback itself has
|
|
126
|
+
an offset
|
|
127
|
+
|
|
128
|
+
## Known-good example
|
|
129
|
+
|
|
130
|
+
Glitch (an image-compositing tool) converted in one pass: `globals.css` went
|
|
131
|
+
229 → 49 lines, its local `components/workspace/` was deleted entirely, and the
|
|
132
|
+
layers panel lost 174 lines by adopting `Tree`.
|
|
133
|
+
|
|
134
|
+
## Read next
|
|
135
|
+
|
|
136
|
+
- [building-a-tool.md](./building-a-tool.md) or
|
|
137
|
+
[building-a-page.md](./building-a-page.md)
|
|
138
|
+
- `DESIGN.md → Registers`
|
|
139
|
+
- `llms/reference/token-intent.md`
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
# Task: create a preset from inspiration
|
|
2
|
+
|
|
3
|
+
A preset is a versioned design input: Sable theme values, font requirements,
|
|
4
|
+
design guidance, and optional attributed observations. It is reusable across
|
|
5
|
+
products. It does not encode a reference site's content or page structure.
|
|
6
|
+
|
|
7
|
+
Use the agent's `inspire` mode with images, URLs, a description, or a combination.
|
|
8
|
+
Inspect the actual supplied evidence. Record typography relationships, surface
|
|
9
|
+
contrast, spacing, shape, imagery treatment, and observed motion. Label each
|
|
10
|
+
reference decision as measured, estimated, derived, or user-edited. A screenshot
|
|
11
|
+
cannot establish animation timing, hidden behavior, or an unseen responsive state.
|
|
12
|
+
|
|
13
|
+
Choose an original direction suitable for the user's future product. Map that
|
|
14
|
+
direction onto the installed Sable token vocabulary. Preserve readable semantic
|
|
15
|
+
roles and supported control behavior rather than recoloring every primitive
|
|
16
|
+
independently. Load fonts through the consuming project's font setup.
|
|
17
|
+
|
|
18
|
+
```bash
|
|
19
|
+
pnpm exec sable preset --template
|
|
20
|
+
pnpm exec sable preset --check .sable/presets/clay.json --json
|
|
21
|
+
pnpm exec sable preset --css .sable/presets/clay.json
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
The template starts from a complete Base theme snapshot. Give it a unique ID and
|
|
25
|
+
label; author its tokens, direction, and reference observations. Avoid IDs of
|
|
26
|
+
bundled themes. Save the record at `.sable/presets/<id>.json`. Advance `revision`
|
|
27
|
+
when changing a shared record; keep earlier revisions when reproducibility matters.
|
|
28
|
+
|
|
29
|
+
Preview using real Sable components or import the JSON in Canvas. Inspect both
|
|
30
|
+
light and dark modes and test long text, focus, disabled controls, floating
|
|
31
|
+
surfaces, and errors. Token validation checks the contract and safe CSS structure;
|
|
32
|
+
rendered review establishes contrast, coherence, and suitability.
|
|
33
|
+
|
|
34
|
+
Canvas's Export view downloads this same format with resolved theme values and
|
|
35
|
+
the current customizations. Enter a name and design guidance, save the JSON, and
|
|
36
|
+
place it in the target project. The browser cannot write into that project for
|
|
37
|
+
the agent. An imported preset can be edited and downloaded as a new revision.
|
|
38
|
+
|
|
39
|
+
Deliver the preset, its component preview, fonts to load, and any unverified
|
|
40
|
+
observations. Stop before building a full product. The next step is a concrete
|
|
41
|
+
`build <idea> using <preset file>` request, described in
|
|
42
|
+
[building-with-sable.md](./building-with-sable.md) and
|
|
43
|
+
[using-presets.md](./using-presets.md).
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
# Task: create assets that carry the design
|
|
2
|
+
|
|
3
|
+
Use this workflow when imagery, illustration, or type materially establishes the
|
|
4
|
+
surface's identity or explains its content. A useful asset changes what someone
|
|
5
|
+
understands or feels. Adding a decorative image to every section is unnecessary.
|
|
6
|
+
|
|
7
|
+
## Define the asset's job before producing it
|
|
8
|
+
|
|
9
|
+
Keep the asset plan with the surface brief, for example `design/<surface>/assets.json`.
|
|
10
|
+
The optional [asset-plan template](../taste/asset-plan.template.json) is a plain
|
|
11
|
+
planning record, not a `sable grade` or `sable explore` schema. Fill only relevant
|
|
12
|
+
fields; `null` means unresolved, never tested or zero cost.
|
|
13
|
+
|
|
14
|
+
For each important asset, specify:
|
|
15
|
+
|
|
16
|
+
| Decision | Concrete example |
|
|
17
|
+
|---|---|
|
|
18
|
+
| Role and message | A workshop photograph shows the scale of the table and what a visitor will make. |
|
|
19
|
+
| Subject and composition | Hands working clay at lower right; clear space at upper left for the heading. |
|
|
20
|
+
| Visual treatment | Side lighting, warm paper background, shallow shadows; retain clay texture and avoid a glossy product-render finish. |
|
|
21
|
+
| Relationship to the set | Product illustrations share line weight and viewpoint while their silhouettes distinguish the actual varieties. |
|
|
22
|
+
| Placement and crop | Wide establishing view on desktop; a tighter alternate composition on mobile that retains both hands and the object. |
|
|
23
|
+
| Source and readiness | User-provided photo with confirmed usage scope; original SVG; generated illustration; or explicitly provisional placeholder. |
|
|
24
|
+
| Delivery target | Expected display dimensions, format candidates, byte budget, and loading priority. |
|
|
25
|
+
|
|
26
|
+
Words such as "premium" or "editorial" need these visible decisions to become
|
|
27
|
+
actionable. For an exact reference reconstruction, preserve its imagery roles and
|
|
28
|
+
framing; report unavailable source assets or substitutions. Atlas captures are
|
|
29
|
+
study evidence, not a product asset library.
|
|
30
|
+
|
|
31
|
+
## Produce the right material
|
|
32
|
+
|
|
33
|
+
- Use provided assets when they fit the brief and permitted usage. Record their
|
|
34
|
+
source, creator when known, license or permission reference, attribution needs,
|
|
35
|
+
and any unresolved usage restriction. Finding an image online does not establish
|
|
36
|
+
permission to ship it. Do not label unresolved rights as cleared.
|
|
37
|
+
- Use original SVG or other code-native art for diagrams, marks, or illustration
|
|
38
|
+
systems that benefit from controlled geometry. Build a representative set before
|
|
39
|
+
repeating it: six recolored versions of one symbol may fail to distinguish six
|
|
40
|
+
subjects. Informative marks must not imply inaccurate counts or measurements.
|
|
41
|
+
- Use an available image-generation workflow for original raster material when
|
|
42
|
+
it serves the brief. Include subject, framing, light, material, palette, crop
|
|
43
|
+
constraints, and exclusions in the prompt. Inspect the actual result; a prompt
|
|
44
|
+
is not an asset. Record generated origin honestly. Generated scenes are not
|
|
45
|
+
evidence of a real customer's product, venue, testimonial, or outcome.
|
|
46
|
+
- For video, define the subject, shot sequence or loop, pacing, crop, poster, and
|
|
47
|
+
whether sound is meaningful. Use supplied, appropriately sourced, or generated
|
|
48
|
+
footage within the task's scope. Keep a usable poster/fallback. Motion review is
|
|
49
|
+
covered in [reviewing-motion.md](./reviewing-motion.md).
|
|
50
|
+
|
|
51
|
+
For fonts, record family and role, required weights, language/glyph coverage,
|
|
52
|
+
source, and relevant license. Load the actual files through the app's supported
|
|
53
|
+
font pipeline, such as local `@font-face`, a framework loader, or a provider link.
|
|
54
|
+
Declaring a font in a theme does not load it. Verify the font system and rendered
|
|
55
|
+
text, including fallback, long titles, numerals, and relevant accented characters.
|
|
56
|
+
An unavailable face needs an honest substitute rather than a broken provider URL.
|
|
57
|
+
|
|
58
|
+
## Compose, crop, and deliver
|
|
59
|
+
|
|
60
|
+
Test assets in the layout early with real copy. Choose the image focal point and
|
|
61
|
+
text position together. Use `srcset`/`sizes` for resolution choices and `<picture>`
|
|
62
|
+
or another explicit art-direction mechanism when the mobile composition should
|
|
63
|
+
change. Inspect crops at the actual breakpoints: scaling a wide frame can remove
|
|
64
|
+
its subject or leave unusably tiny detail.
|
|
65
|
+
|
|
66
|
+
Give meaningful images useful alternative text; keep decorative imagery out of
|
|
67
|
+
the reading order. Avoid baking essential copy into an image when live text can
|
|
68
|
+
carry it. Provide equivalent information for a diagram or meaningful video.
|
|
69
|
+
|
|
70
|
+
Choose a project-specific byte budget, including the combined initial image and
|
|
71
|
+
font payload. Measure actual output bytes. Compare formats and encodings at the
|
|
72
|
+
required size; retain a lossless or vector original when useful, and inspect the
|
|
73
|
+
optimized export for damaged edges, banding, small-text loss, or changed color.
|
|
74
|
+
Do not preload every asset or lazy-load the primary visual by habit. Verify the
|
|
75
|
+
chosen loading behavior in the actual page, including slow or failed media.
|
|
76
|
+
|
|
77
|
+
A temporary placeholder may unblock composition. Label it in the review/asset
|
|
78
|
+
plan, preserve its intended dimensions, and make missing material clear to the
|
|
79
|
+
reviewer. Replace it before calling that visual complete, or state the remaining
|
|
80
|
+
limitation. Generation prompts and implementation notes belong in authoring
|
|
81
|
+
artifacts, not the finished product flow.
|
|
82
|
+
|
|
83
|
+
## Verify the assembled result
|
|
84
|
+
|
|
85
|
+
Inspect desktop and mobile renders after fonts and media load, then test their
|
|
86
|
+
fallback states. Check that the asset advances the intended message, remains
|
|
87
|
+
recognizable at its smallest use, has a consistent relationship to the set, and
|
|
88
|
+
does not compromise text or controls. Retain before/after evidence when revising
|
|
89
|
+
an asset or crop; changing a file without viewing the new bytes is not review.
|
|
90
|
+
|
|
91
|
+
Record delivered paths, dimensions, bytes, and the captures actually inspected.
|
|
92
|
+
Use [reviewing-a-design.md](./reviewing-a-design.md) to connect those artifacts to
|
|
93
|
+
the build's source hash. Include local asset directories in the fingerprint;
|
|
94
|
+
remote resources need separate provenance because the source hash cannot freeze
|
|
95
|
+
their contents. Reuse a successful treatment as a conditional principle, not a
|
|
96
|
+
requirement that every future brand use the same illustration or font pairing.
|