@syncedco/flow 0.2.0 → 0.3.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +13 -0
- package/README.md +2 -1
- package/bin/synced-flow.mjs +362 -3
- package/components.css +190 -2
- package/docs/api-contract.md +3 -3
- package/docs/audit-2026-06.md +113 -0
- package/docs/config-reference.md +27 -0
- package/docs/patterns.md +7 -0
- package/docs/system-primitives.md +8 -5
- package/docs/tokens.md +7 -1
- package/docs/why-synced-flow.md +1 -1
- package/layout.css +91 -1
- package/package.json +3 -2
- package/skills/synced-flow/SKILL.md +23 -6
- package/src/tokens.mjs +10 -0
- package/styles.css +328 -4
- package/tokens.css +45 -0
- package/utilities.css +2 -1
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
## 0.3.1 - 2026-06-13
|
|
4
|
+
|
|
5
|
+
- Fix mobile app drawer scrolling by giving the drawer sidebar a viewport block size.
|
|
6
|
+
- Vertically center `.sf-tab` contents.
|
|
7
|
+
- Improve built-in dark theme token coverage for primary, state, soft, ring, and glow tokens.
|
|
8
|
+
- Make `.sf-filter-bar` alignment token-configurable with a centered default.
|
|
9
|
+
- Add a status-chip dot opt-out with `data-dot="false"` and `sf-status--label`.
|
|
10
|
+
- Add reduced-motion safeguards for hover lift and marquee animation.
|
|
11
|
+
- Add public `--sf-z-*` stacking tokens for header, backdrop, drawer, overlay, toast, sticky, and skip-link layers.
|
|
12
|
+
- Reframe positioning copy from "AI-native" to "AI-friendly".
|
|
13
|
+
- Document the global theme config scope and the project-CSS pattern for advanced theme matrices.
|
package/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# Synced Flow
|
|
2
2
|
|
|
3
|
-
AI-
|
|
3
|
+
AI-friendly fluid CSS design system for brand-consistent websites, agency builds,
|
|
4
4
|
and modern frontend projects.
|
|
5
5
|
|
|
6
6
|
Synced Flow gives developers and AI coding agents a shared styling contract:
|
|
@@ -58,6 +58,7 @@ documentation, and recipes.
|
|
|
58
58
|
Useful project files:
|
|
59
59
|
|
|
60
60
|
- [Contributing](CONTRIBUTING.md)
|
|
61
|
+
- [Changelog](CHANGELOG.md)
|
|
61
62
|
- [Security policy](SECURITY.md)
|
|
62
63
|
- [Code of conduct](CODE_OF_CONDUCT.md)
|
|
63
64
|
- [Support](SUPPORT.md)
|
package/bin/synced-flow.mjs
CHANGED
|
@@ -1024,8 +1024,8 @@ function getTokenSummary() {
|
|
|
1024
1024
|
components: componentTokens,
|
|
1025
1025
|
themePresets: presetNames.map(kebabThemeName),
|
|
1026
1026
|
starterClasses: {
|
|
1027
|
-
layout: ['sf-container', 'sf-container--narrow', 'sf-container--wide', 'sf-container--full', 'sf-rail-shell', 'sf-rail-shell__rail', 'sf-rail-shell__body', 'sf-rail-shell__mark', 'sf-rail-shell__label', 'sf-section', 'sf-section--compact', 'sf-section--spacious', 'sf-stack', 'sf-flow', 'sf-cluster', 'sf-repel', 'sf-toolbar', 'sf-app-shell', 'sf-app-sidebar', 'sf-app-sidebar__brand', 'sf-app-main', 'sf-auto-grid', 'sf-auto-grid--xs', 'sf-auto-grid--sm', 'sf-auto-grid--lg', 'sf-switcher', 'sf-sidebar', 'sf-split', 'sf-split--reverse', 'sf-chapter', 'sf-chapter__copy', 'sf-frame', 'sf-cover', 'sf-panel-grid', 'sf-metric-grid', 'sf-pipeline', 'sf-scroll-viewport', 'sf-scroll-panel', 'sf-scroll-snap-y', 'sf-sticky-top', 'sf-media-object', 'sf-aside-rail'],
|
|
1028
|
-
components: ['sf-button', 'sf-button-group', 'sf-button--icon', 'sf-icon-button', 'sf-icon', 'sf-icon--xs', 'sf-icon--sm', 'sf-icon--md', 'sf-icon--lg', 'sf-icon--xl', 'sf-card', 'sf-card--flat', 'sf-card--raised', 'sf-card--compact', 'sf-card--glass', 'sf-card--technical', 'sf-card--accent', 'sf-card--center', 'sf-surface', 'sf-code-window', 'sf-code-window--glow', 'sf-code-window__bar', 'sf-window-dots', 'sf-code-lines', 'sf-code-lines--compact', 'sf-code-block', 'sf-token-strip', 'sf-marquee', 'sf-marquee__track', 'sf-marquee__item', 'sf-command-list', 'sf-command-list__prompt', 'sf-platform-card', 'sf-platform-card__logo', 'sf-hero', 'sf-logo-cloud', 'sf-feature', 'sf-stats', 'sf-stat', 'sf-testimonial', 'sf-pricing-grid', 'sf-price-card', 'sf-price', 'sf-faq', 'sf-cta', 'sf-footer', 'sf-badge', 'sf-avatar', 'sf-chart', 'sf-chart--wide', 'sf-chart__plot', 'sf-chart__svg', 'sf-chart__grid', 'sf-chart__axis', 'sf-chart__area', 'sf-chart__line', 'sf-chart__point', 'sf-chart__legend', 'sf-chart__legend-item', 'sf-chart__swatch', 'sf-meter-list', 'sf-meter-row', 'sf-meter', 'sf-progress', 'sf-skeleton', 'sf-filter-bar', 'sf-data-table', 'sf-status', 'sf-data-list', 'sf-empty-state', 'sf-settings-section', 'sf-detail-panel', 'sf-nav', 'sf-nav--mobile', 'sf-nav__list', 'sf-nav__link', 'sf-menu', 'sf-breadcrumb', 'sf-pagination', 'sf-dialog', 'sf-popover', 'sf-tooltip', 'sf-tooltip-trigger', 'sf-menu-popover', 'sf-toast', 'sf-banner', 'sf-drawer', 'sf-drawer--stack', 'sf-disclosure', 'sf-accordion', 'sf-tabs', 'sf-tab-list', 'sf-tab', 'sf-tab-panel', 'sf-form', 'sf-fieldset', 'sf-field', 'sf-label', 'sf-help', 'sf-error', 'sf-input', 'sf-select', 'sf-textarea', 'sf-check', 'sf-alert', 'sf-section-header', 'sf-kicker'],
|
|
1027
|
+
layout: ['sf-container', 'sf-container--narrow', 'sf-container--wide', 'sf-container--full', 'sf-rail-shell', 'sf-rail-shell__rail', 'sf-rail-shell__body', 'sf-rail-shell__mark', 'sf-rail-shell__label', 'sf-section', 'sf-section--compact', 'sf-section--spacious', 'sf-stack', 'sf-flow', 'sf-cluster', 'sf-repel', 'sf-toolbar', 'sf-app-shell', 'sf-app-sidebar', 'sf-app-sidebar__brand', 'sf-app-header', 'sf-app-backdrop', 'sf-app-main', 'sf-auto-grid', 'sf-auto-grid--xs', 'sf-auto-grid--sm', 'sf-auto-grid--lg', 'sf-switcher', 'sf-sidebar', 'sf-split', 'sf-split--reverse', 'sf-chapter', 'sf-chapter__copy', 'sf-frame', 'sf-cover', 'sf-panel-grid', 'sf-metric-grid', 'sf-pipeline', 'sf-scroll-viewport', 'sf-scroll-panel', 'sf-scroll-snap-y', 'sf-sticky-top', 'sf-media-object', 'sf-aside-rail'],
|
|
1028
|
+
components: ['sf-button', 'sf-button-group', 'sf-button--icon', 'sf-icon-button', 'sf-icon', 'sf-icon--xs', 'sf-icon--sm', 'sf-icon--md', 'sf-icon--lg', 'sf-icon--xl', 'sf-card', 'sf-card--flat', 'sf-card--raised', 'sf-card--compact', 'sf-card--glass', 'sf-card--technical', 'sf-card--accent', 'sf-card--center', 'sf-surface', 'sf-code-window', 'sf-code-window--glow', 'sf-code-window__bar', 'sf-window-dots', 'sf-code-lines', 'sf-code-lines--compact', 'sf-code-block', 'sf-token-strip', 'sf-marquee', 'sf-marquee__track', 'sf-marquee__item', 'sf-command-list', 'sf-command-list__prompt', 'sf-platform-card', 'sf-platform-card__logo', 'sf-hero', 'sf-logo-cloud', 'sf-feature', 'sf-stats', 'sf-stat', 'sf-testimonial', 'sf-pricing-grid', 'sf-price-card', 'sf-price', 'sf-faq', 'sf-cta', 'sf-footer', 'sf-badge', 'sf-avatar', 'sf-chart', 'sf-chart--wide', 'sf-chart__plot', 'sf-chart__svg', 'sf-chart__grid', 'sf-chart__axis', 'sf-chart__area', 'sf-chart__line', 'sf-chart__point', 'sf-chart__legend', 'sf-chart__legend-item', 'sf-chart__swatch', 'sf-meter-list', 'sf-meter-row', 'sf-meter', 'sf-progress', 'sf-skeleton', 'sf-filter-bar', 'sf-search', 'sf-data-table', 'sf-table-sort', 'sf-status', 'sf-data-list', 'sf-empty-state', 'sf-settings-section', 'sf-detail-panel', 'sf-nav', 'sf-nav--mobile', 'sf-nav__list', 'sf-nav__link', 'sf-menu', 'sf-breadcrumb', 'sf-pagination', 'sf-dialog', 'sf-popover', 'sf-tooltip', 'sf-tooltip-trigger', 'sf-menu-popover', 'sf-toast', 'sf-toast-stack', 'sf-banner', 'sf-drawer', 'sf-drawer--stack', 'sf-disclosure', 'sf-accordion', 'sf-tabs', 'sf-tab-list', 'sf-tab', 'sf-tab__count', 'sf-tab-panel', 'sf-form', 'sf-fieldset', 'sf-field', 'sf-label', 'sf-help', 'sf-error', 'sf-input', 'sf-select', 'sf-textarea', 'sf-check', 'sf-alert', 'sf-section-header', 'sf-kicker'],
|
|
1029
1029
|
type: ['sf-text-caption', 'sf-text-body', 'sf-text-lead', 'sf-text-h4', 'sf-text-h3', 'sf-text-h2', 'sf-text-h1', 'sf-text-display'],
|
|
1030
1030
|
utilities: ['sr-only', 'not-sr-only', 'sf-visually-hidden', 'sf-not-visually-hidden', 'sf-skip-link', 'sf-focus-ring', 'sf-touch-target', 'sf-list-reset', 'sf-list-disc', 'sf-list-decimal', 'sf-link', 'sf-link-subtle', 'sf-link-plain', 'sf-prose', 'sf-prose--blog', 'sf-prose--legal', 'sf-meta', 'sf-figure', 'sf-caption', 'sf-table-wrap', 'sf-push-block-end', 'sf-push-block-start', 'sf-push-inline-end', 'sf-push-inline-start', 'sf-full-bleed', 'sf-text-muted', 'sf-bg-surface', 'sf-border', 'sf-rounded-panel', 'sf-shadow-md', 'sf-animate-fade', 'sf-animate-rise', 'sf-animate-scale', 'sf-animate-slide', 'sf-animate-stagger'],
|
|
1031
1031
|
},
|
|
@@ -1239,6 +1239,133 @@ export default function DialogExample() {
|
|
|
1239
1239
|
markup: '<article class="sf-container sf-prose sf-prose--blog">...</article>',
|
|
1240
1240
|
keywords: ['blog', 'article', 'prose', 'legal', 'docs', 'case-study', 'table', 'figure', 'seo'],
|
|
1241
1241
|
},
|
|
1242
|
+
{
|
|
1243
|
+
id: 'app-shell-layout',
|
|
1244
|
+
name: 'App shell layout',
|
|
1245
|
+
whenToUse: 'Product apps, portals, admin tools, and dashboards that need a header, sidebar, main workspace, optional collapsed sidebar, and mobile drawer mode.',
|
|
1246
|
+
classes: ['sf-app-shell', 'sf-app-header', 'sf-app-sidebar', 'sf-app-backdrop', 'sf-app-main', 'sf-nav', 'sf-nav__link'],
|
|
1247
|
+
requiresJs: false,
|
|
1248
|
+
requiresJsNotes: 'CSS handles layout states. The consuming app owns toggling data-collapsed and data-sidebar-open attributes.',
|
|
1249
|
+
a11y: ['Keep the sidebar inside a named nav or aside.', 'Use a real button to toggle drawer/collapsed states.', 'Move focus into the drawer when app JavaScript opens it.'],
|
|
1250
|
+
gotchas: ['Use data-mobile="drawer" only for app layouts that need overlay navigation on small screens.', 'Keep data-hide-collapsed text available through aria-labels on icon-only links.'],
|
|
1251
|
+
markup: {
|
|
1252
|
+
html: `<div class="sf-app-shell" data-layout="fixed-header" data-mobile="drawer">
|
|
1253
|
+
<header class="sf-app-header"><button class="sf-button sf-button--ghost" type="button" aria-label="Open navigation">Menu</button><strong>Workspace</strong></header>
|
|
1254
|
+
<aside class="sf-app-sidebar" aria-label="Workspace navigation"><nav class="sf-nav"><a class="sf-nav__link" href="/app" aria-current="page">Dashboard</a></nav></aside>
|
|
1255
|
+
<button class="sf-app-backdrop" type="button" aria-label="Close navigation"></button>
|
|
1256
|
+
<main class="sf-app-main">...</main>
|
|
1257
|
+
</div>`,
|
|
1258
|
+
},
|
|
1259
|
+
keywords: ['app', 'shell', 'layout', 'sidebar', 'header', 'drawer', 'collapsed', 'dashboard', 'admin', 'portal'],
|
|
1260
|
+
},
|
|
1261
|
+
{
|
|
1262
|
+
id: 'row-action-menu',
|
|
1263
|
+
name: 'Row action menu',
|
|
1264
|
+
whenToUse: 'Kebab, overflow, and per-row action menus using native popover markup and destructive item styling.',
|
|
1265
|
+
classes: ['sf-menu-popover', 'sf-menu', 'sf-button', 'sf-button--ghost', 'sf-button--icon'],
|
|
1266
|
+
requiresJs: false,
|
|
1267
|
+
requiresJsNotes: 'Native popover handles open, backdrop dismiss, and light-dismiss where supported. App JavaScript may add keyboard roving for complex menus.',
|
|
1268
|
+
a11y: ['Use a button trigger with an accessible label.', 'Keep destructive actions visibly labelled.', 'Use links for navigation and buttons for actions.'],
|
|
1269
|
+
gotchas: ['Use popovertarget on buttons, not anchors.', 'The app owns confirmation flows for destructive actions.'],
|
|
1270
|
+
markup: {
|
|
1271
|
+
html: `<button class="sf-button sf-button--ghost sf-button--icon" type="button" popovertarget="row-actions-1" aria-label="Open row actions">...</button>
|
|
1272
|
+
<div class="sf-menu-popover" id="row-actions-1" popover="auto">
|
|
1273
|
+
<nav class="sf-menu" aria-label="Row actions">
|
|
1274
|
+
<a href="/items/1">Open</a>
|
|
1275
|
+
<button type="button">Archive</button>
|
|
1276
|
+
<button type="button" data-variant="danger">Delete</button>
|
|
1277
|
+
</nav>
|
|
1278
|
+
</div>`,
|
|
1279
|
+
},
|
|
1280
|
+
keywords: ['menu', 'row', 'actions', 'kebab', 'popover', 'destructive', 'table'],
|
|
1281
|
+
},
|
|
1282
|
+
{
|
|
1283
|
+
id: 'tabs-with-counts',
|
|
1284
|
+
name: 'Tabs with counts',
|
|
1285
|
+
whenToUse: 'Filtered app views, inboxes, queues, and reports where each tab needs a compact count.',
|
|
1286
|
+
classes: ['sf-tabs', 'sf-tab-list', 'sf-tab', 'sf-tab__count', 'sf-tab-panel'],
|
|
1287
|
+
requiresJs: false,
|
|
1288
|
+
requiresJsNotes: 'CSS styles state. The app owns aria-selected, routing, or tab panel switching for dynamic interfaces.',
|
|
1289
|
+
a11y: ['Use role="tablist" only when implementing full keyboard tab behavior.', 'Use aria-selected on active tabs.', 'Keep counts textual and not colour-only.'],
|
|
1290
|
+
gotchas: ['Use data-variant="pill" for pill tabs.', 'Use links for route-backed tabs and buttons for in-page tab switching.'],
|
|
1291
|
+
markup: {
|
|
1292
|
+
html: `<nav class="sf-tab-list" aria-label="Views">
|
|
1293
|
+
<a class="sf-tab" data-variant="pill" href="?view=open" aria-selected="true">Open <span class="sf-tab__count">12</span></a>
|
|
1294
|
+
<a class="sf-tab" data-variant="pill" href="?view=closed">Closed <span class="sf-tab__count">8</span></a>
|
|
1295
|
+
</nav>`,
|
|
1296
|
+
},
|
|
1297
|
+
keywords: ['tabs', 'counts', 'queue', 'views', 'filters', 'pill'],
|
|
1298
|
+
},
|
|
1299
|
+
{
|
|
1300
|
+
id: 'data-table-actions',
|
|
1301
|
+
name: 'Data table actions',
|
|
1302
|
+
whenToUse: 'Operational tables that need sortable header controls, clickable rows, and a responsive card view fallback.',
|
|
1303
|
+
classes: ['sf-table-wrap', 'sf-data-table', 'sf-table-sort', 'sf-status', 'sf-link'],
|
|
1304
|
+
requiresJs: false,
|
|
1305
|
+
requiresJsNotes: 'Sorting and row navigation are app behavior. Synced Flow only styles controls, focus state, and responsive card layout.',
|
|
1306
|
+
a11y: ['Use aria-sort on sorted columns.', 'Keep the row destination as a real link.', 'Add data-label to cells when using card fallback.'],
|
|
1307
|
+
gotchas: ['Set data-view="cards" on sf-table-wrap for the responsive card fallback.', 'Do not put interactive controls inside a stretched row link.'],
|
|
1308
|
+
markup: {
|
|
1309
|
+
html: `<div class="sf-table-wrap" data-view="cards">
|
|
1310
|
+
<table class="sf-data-table">
|
|
1311
|
+
<thead><tr><th><button class="sf-table-sort" type="button" aria-sort="ascending">Name</button></th><th>Status</th></tr></thead>
|
|
1312
|
+
<tbody><tr data-clickable="true"><td data-label="Name"><a class="sf-link" href="/items/1">Northstar</a></td><td data-label="Status"><span class="sf-status" data-size="sm" data-tone="success">Active</span></td></tr></tbody>
|
|
1313
|
+
</table>
|
|
1314
|
+
</div>`,
|
|
1315
|
+
},
|
|
1316
|
+
keywords: ['table', 'sort', 'sortable', 'responsive', 'card', 'clickable', 'data'],
|
|
1317
|
+
},
|
|
1318
|
+
{
|
|
1319
|
+
id: 'search-field',
|
|
1320
|
+
name: 'Search field',
|
|
1321
|
+
whenToUse: 'Search inputs in filters, headers, command surfaces, and dense app interfaces.',
|
|
1322
|
+
classes: ['sf-search', 'sf-field', 'sf-label', 'sf-input'],
|
|
1323
|
+
requiresJs: false,
|
|
1324
|
+
requiresJsNotes: 'Native form submit works without JavaScript. Debounce, suggestions, and command logic belong to the app.',
|
|
1325
|
+
a11y: ['Keep a label available, visible or visually hidden.', 'Use type="search".', 'Do not use placeholder text as the only label.'],
|
|
1326
|
+
gotchas: ['Use sf-field data-density="compact" for dense interfaces.', 'Autocomplete and combobox behavior is app-owned.'],
|
|
1327
|
+
markup: {
|
|
1328
|
+
html: `<label class="sf-field sf-search" data-density="compact">
|
|
1329
|
+
<span class="sf-label">Search</span>
|
|
1330
|
+
<input class="sf-input" type="search" name="q" placeholder="Search records">
|
|
1331
|
+
</label>`,
|
|
1332
|
+
},
|
|
1333
|
+
keywords: ['search', 'input', 'filter', 'command', 'dense'],
|
|
1334
|
+
},
|
|
1335
|
+
{
|
|
1336
|
+
id: 'modal-form',
|
|
1337
|
+
name: 'Modal form',
|
|
1338
|
+
whenToUse: 'Dialog-based create, edit, confirmation, and settings flows with form body and footer actions.',
|
|
1339
|
+
classes: ['sf-dialog', 'sf-dialog__header', 'sf-dialog__body', 'sf-dialog__footer', 'sf-form', 'sf-field', 'sf-input', 'sf-button'],
|
|
1340
|
+
requiresJs: true,
|
|
1341
|
+
requiresJsNotes: 'Native dialog provides modal behavior. Broad support still needs a tiny app handler for showModal() until commandfor is universal.',
|
|
1342
|
+
a11y: ['Use a visible dialog heading.', 'Keep footer actions inside the form or associated with form attribute.', 'Return focus to the trigger after closing.'],
|
|
1343
|
+
gotchas: ['Use data-layout="form" for form spacing.', 'Validation and persistence belong to the app.'],
|
|
1344
|
+
markup: {
|
|
1345
|
+
html: `<dialog class="sf-dialog" data-layout="form" id="edit-dialog" aria-labelledby="edit-title">
|
|
1346
|
+
<header class="sf-dialog__header"><h2 id="edit-title">Edit record</h2></header>
|
|
1347
|
+
<form class="sf-form sf-dialog__body" id="edit-form"><label class="sf-field"><span class="sf-label">Name</span><input class="sf-input" name="name"></label></form>
|
|
1348
|
+
<footer class="sf-dialog__footer"><button class="sf-button sf-button--outline" formmethod="dialog">Cancel</button><button class="sf-button" form="edit-form">Save</button></footer>
|
|
1349
|
+
</dialog>`,
|
|
1350
|
+
},
|
|
1351
|
+
keywords: ['dialog', 'modal', 'form', 'drawer', 'actions', 'footer'],
|
|
1352
|
+
},
|
|
1353
|
+
{
|
|
1354
|
+
id: 'toast-stack',
|
|
1355
|
+
name: 'Toast stack',
|
|
1356
|
+
whenToUse: 'Non-blocking app feedback where multiple toast messages need a consistent fixed stack layout.',
|
|
1357
|
+
classes: ['sf-toast-stack', 'sf-toast', 'sf-alert', 'sf-alert__title'],
|
|
1358
|
+
requiresJs: false,
|
|
1359
|
+
requiresJsNotes: 'Stack layout is CSS. Queuing, dismissal timing, and live-region announcements belong to the app.',
|
|
1360
|
+
a11y: ['Use an aria-live region for dynamic messages.', 'Do not auto-dismiss critical errors too quickly.', 'Provide a close control when messages persist.'],
|
|
1361
|
+
gotchas: ['Use sf-toast-stack for layout and sf-toast or sf-alert for each item.', 'Keep queue state outside Synced Flow.'],
|
|
1362
|
+
markup: {
|
|
1363
|
+
html: `<div class="sf-toast-stack" aria-live="polite" aria-label="Notifications">
|
|
1364
|
+
<section class="sf-toast sf-alert" role="status"><strong class="sf-alert__title">Saved</strong><p>Changes were updated.</p></section>
|
|
1365
|
+
</div>`,
|
|
1366
|
+
},
|
|
1367
|
+
keywords: ['toast', 'notification', 'stack', 'feedback', 'alert'],
|
|
1368
|
+
},
|
|
1242
1369
|
{
|
|
1243
1370
|
id: 'filter-toolbar',
|
|
1244
1371
|
name: 'Filter toolbar',
|
|
@@ -2174,8 +2301,14 @@ function runRecipe() {
|
|
|
2174
2301
|
|
|
2175
2302
|
function findPattern(value, patterns) {
|
|
2176
2303
|
const normal = value.toLowerCase()
|
|
2304
|
+
const aliases = {
|
|
2305
|
+
form: 'contact-form',
|
|
2306
|
+
}
|
|
2307
|
+
|
|
2177
2308
|
return (
|
|
2309
|
+
patterns.find((pattern) => pattern.id === aliases[normal]) ??
|
|
2178
2310
|
patterns.find((pattern) => pattern.id === normal) ??
|
|
2311
|
+
patterns.find((pattern) => pattern.id.endsWith(`-${normal}`)) ??
|
|
2179
2312
|
patterns.find((pattern) => pattern.keywords.some((keyword) => keyword === normal)) ??
|
|
2180
2313
|
patterns.find((pattern) => pattern.name.toLowerCase().includes(normal))
|
|
2181
2314
|
)
|
|
@@ -2685,7 +2818,9 @@ const semanticColours = {
|
|
|
2685
2818
|
destructive: 'var(--color-destructive)',
|
|
2686
2819
|
'destructive-foreground': 'var(--color-destructive-foreground)',
|
|
2687
2820
|
border: 'var(--color-border)',
|
|
2821
|
+
'border-subtle': 'var(--color-border-subtle)',
|
|
2688
2822
|
input: 'var(--color-input)',
|
|
2823
|
+
backdrop: 'var(--color-backdrop)',
|
|
2689
2824
|
ring: 'var(--color-ring)',
|
|
2690
2825
|
heading: 'var(--color-heading)',
|
|
2691
2826
|
'page-text': 'var(--color-page-text)',
|
|
@@ -2694,7 +2829,15 @@ const semanticColours = {
|
|
|
2694
2829
|
subdued: 'var(--color-subdued)',
|
|
2695
2830
|
surface: 'var(--color-surface)',
|
|
2696
2831
|
'surface-alt': 'var(--color-surface-alt)',
|
|
2832
|
+
'surface-hover': 'var(--color-surface-hover)',
|
|
2833
|
+
'surface-raised-2': 'var(--color-surface-raised-2)',
|
|
2697
2834
|
'hero-bg': 'var(--color-hero-bg)',
|
|
2835
|
+
'categorical-1': 'var(--color-categorical-1)',
|
|
2836
|
+
'categorical-2': 'var(--color-categorical-2)',
|
|
2837
|
+
'categorical-3': 'var(--color-categorical-3)',
|
|
2838
|
+
'categorical-4': 'var(--color-categorical-4)',
|
|
2839
|
+
'categorical-5': 'var(--color-categorical-5)',
|
|
2840
|
+
'categorical-6': 'var(--color-categorical-6)',
|
|
2698
2841
|
green: 'var(--color-green)',
|
|
2699
2842
|
'green-dark': 'var(--color-green-dark)',
|
|
2700
2843
|
'green-muted': 'var(--color-green-muted)',
|
|
@@ -3800,6 +3943,7 @@ function buildTokensCss() {
|
|
|
3800
3943
|
--font-mono: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", monospace;
|
|
3801
3944
|
|
|
3802
3945
|
${steps.join('\n')}
|
|
3946
|
+
--type-caption-sm: var(--step--2);
|
|
3803
3947
|
|
|
3804
3948
|
${spaces.join('\n')}
|
|
3805
3949
|
${pairs.join('\n')}
|
|
@@ -3850,7 +3994,9 @@ ${pairs.join('\n')}
|
|
|
3850
3994
|
--color-destructive: var(--sf-colour-destructive, ${colours.red600});
|
|
3851
3995
|
--color-destructive-foreground: var(--sf-colour-destructive-foreground, oklch(98% 0.01 95));
|
|
3852
3996
|
--color-border: var(--sf-colour-border, oklch(100% 0 0 / 0.12));
|
|
3997
|
+
--color-border-subtle: var(--sf-colour-border-subtle, oklch(100% 0 0 / 0.08));
|
|
3853
3998
|
--color-input: var(--sf-colour-input, oklch(100% 0 0 / 0.12));
|
|
3999
|
+
--color-backdrop: var(--sf-colour-backdrop, oklch(0% 0 0 / 0.56));
|
|
3854
4000
|
--color-ring: var(--sf-colour-ring, ${colours.orange600});
|
|
3855
4001
|
--color-heading: var(--sf-colour-heading, oklch(20% 0.02 18));
|
|
3856
4002
|
--color-page-text: var(--sf-colour-page-text, oklch(23% 0.02 18));
|
|
@@ -3859,6 +4005,14 @@ ${pairs.join('\n')}
|
|
|
3859
4005
|
--color-subdued: var(--sf-colour-subdued, oklch(42% 0.02 20));
|
|
3860
4006
|
--color-surface: var(--sf-colour-surface, ${colours.surfaceAlt});
|
|
3861
4007
|
--color-surface-alt: var(--sf-colour-surface-alt, ${colours.paperAlt});
|
|
4008
|
+
--color-surface-hover: var(--sf-colour-surface-hover, color-mix(in oklch, var(--color-surface-alt) 86%, var(--color-primary)));
|
|
4009
|
+
--color-surface-raised-2: var(--sf-colour-surface-raised-2, color-mix(in oklch, var(--color-surface) 86%, white));
|
|
4010
|
+
--color-categorical-1: var(--sf-colour-categorical-1, var(--color-primary));
|
|
4011
|
+
--color-categorical-2: var(--sf-colour-categorical-2, var(--color-green));
|
|
4012
|
+
--color-categorical-3: var(--sf-colour-categorical-3, var(--color-deco-blue));
|
|
4013
|
+
--color-categorical-4: var(--sf-colour-categorical-4, var(--color-deco-lime));
|
|
4014
|
+
--color-categorical-5: var(--sf-colour-categorical-5, var(--color-warm-icon));
|
|
4015
|
+
--color-categorical-6: var(--sf-colour-categorical-6, var(--color-purple-icon));
|
|
3862
4016
|
--color-hero-bg: oklch(8% 0.02 260);
|
|
3863
4017
|
--color-green: var(--sf-colour-green, ${colours.green600});
|
|
3864
4018
|
--color-green-dark: var(--sf-colour-green-dark, ${colours.green700});
|
|
@@ -4052,11 +4206,47 @@ function buildLayoutCss() {
|
|
|
4052
4206
|
}
|
|
4053
4207
|
|
|
4054
4208
|
.sf-app-shell {
|
|
4209
|
+
--sf-app-header-height: 4rem;
|
|
4210
|
+
--sf-app-sidebar-collapsed-width: 4.75rem;
|
|
4055
4211
|
background: var(--color-background);
|
|
4056
4212
|
color: var(--color-foreground);
|
|
4057
4213
|
display: grid;
|
|
4058
4214
|
grid-template-columns: minmax(14rem, var(--sf-app-sidebar-width, 16rem)) minmax(0, 1fr);
|
|
4059
4215
|
min-block-size: 100dvh;
|
|
4216
|
+
position: relative;
|
|
4217
|
+
transition: grid-template-columns var(--duration-normal) var(--ease-standard);
|
|
4218
|
+
}
|
|
4219
|
+
|
|
4220
|
+
.sf-app-header {
|
|
4221
|
+
align-items: center;
|
|
4222
|
+
background: var(--color-card);
|
|
4223
|
+
border-block-end: 1px solid var(--color-border);
|
|
4224
|
+
display: flex;
|
|
4225
|
+
gap: var(--space-s);
|
|
4226
|
+
grid-column: 1 / -1;
|
|
4227
|
+
justify-content: space-between;
|
|
4228
|
+
min-block-size: var(--sf-app-header-height);
|
|
4229
|
+
padding-inline: var(--space-s-m);
|
|
4230
|
+
z-index: 20;
|
|
4231
|
+
}
|
|
4232
|
+
|
|
4233
|
+
.sf-app-shell[data-layout~="fixed-header"] {
|
|
4234
|
+
grid-template-rows: var(--sf-app-header-height) minmax(0, 1fr);
|
|
4235
|
+
}
|
|
4236
|
+
|
|
4237
|
+
.sf-app-shell[data-layout~="fixed-header"] .sf-app-header {
|
|
4238
|
+
inset-block-start: 0;
|
|
4239
|
+
position: sticky;
|
|
4240
|
+
}
|
|
4241
|
+
|
|
4242
|
+
.sf-app-shell[data-layout~="fixed-header"] .sf-app-sidebar {
|
|
4243
|
+
inset-block-start: var(--sf-app-header-height);
|
|
4244
|
+
max-block-size: calc(100dvh - var(--sf-app-header-height));
|
|
4245
|
+
min-block-size: calc(100dvh - var(--sf-app-header-height));
|
|
4246
|
+
}
|
|
4247
|
+
|
|
4248
|
+
.sf-app-shell[data-collapsed="true"] {
|
|
4249
|
+
grid-template-columns: var(--sf-app-sidebar-collapsed-width) minmax(0, 1fr);
|
|
4060
4250
|
}
|
|
4061
4251
|
|
|
4062
4252
|
.sf-app-sidebar {
|
|
@@ -4072,6 +4262,7 @@ function buildLayoutCss() {
|
|
|
4072
4262
|
padding: var(--space-s);
|
|
4073
4263
|
position: sticky;
|
|
4074
4264
|
inset-block-start: 0;
|
|
4265
|
+
transition: transform var(--duration-normal) var(--ease-standard), inline-size var(--duration-normal) var(--ease-standard);
|
|
4075
4266
|
}
|
|
4076
4267
|
|
|
4077
4268
|
.sf-app-sidebar__brand {
|
|
@@ -4124,6 +4315,58 @@ function buildLayoutCss() {
|
|
|
4124
4315
|
padding: var(--sf-app-main-padding, var(--space-m-l));
|
|
4125
4316
|
}
|
|
4126
4317
|
|
|
4318
|
+
.sf-app-shell[data-collapsed="true"] .sf-app-sidebar {
|
|
4319
|
+
overflow-x: hidden;
|
|
4320
|
+
}
|
|
4321
|
+
|
|
4322
|
+
.sf-app-shell[data-collapsed="true"] .sf-app-sidebar__brand,
|
|
4323
|
+
.sf-app-shell[data-collapsed="true"] .sf-app-sidebar .sf-nav__link {
|
|
4324
|
+
justify-content: center;
|
|
4325
|
+
}
|
|
4326
|
+
|
|
4327
|
+
.sf-app-shell[data-collapsed="true"] [data-hide-collapsed="true"] {
|
|
4328
|
+
display: none;
|
|
4329
|
+
}
|
|
4330
|
+
|
|
4331
|
+
.sf-app-backdrop {
|
|
4332
|
+
display: none;
|
|
4333
|
+
}
|
|
4334
|
+
|
|
4335
|
+
@media (max-width: 48rem) {
|
|
4336
|
+
.sf-app-shell[data-mobile="drawer"] {
|
|
4337
|
+
display: block;
|
|
4338
|
+
}
|
|
4339
|
+
|
|
4340
|
+
.sf-app-shell[data-mobile="drawer"] .sf-app-header {
|
|
4341
|
+
position: sticky;
|
|
4342
|
+
inset-block-start: 0;
|
|
4343
|
+
}
|
|
4344
|
+
|
|
4345
|
+
.sf-app-shell[data-mobile="drawer"] .sf-app-sidebar {
|
|
4346
|
+
box-shadow: var(--shadow-xl);
|
|
4347
|
+
inline-size: min(18rem, calc(100vw - var(--space-l)));
|
|
4348
|
+
inset-block: 0;
|
|
4349
|
+
inset-inline-start: 0;
|
|
4350
|
+
max-block-size: none;
|
|
4351
|
+
min-block-size: 100dvh;
|
|
4352
|
+
position: fixed;
|
|
4353
|
+
transform: translateX(-100%);
|
|
4354
|
+
z-index: 30;
|
|
4355
|
+
}
|
|
4356
|
+
|
|
4357
|
+
.sf-app-shell[data-mobile="drawer"][data-sidebar-open="true"] .sf-app-sidebar {
|
|
4358
|
+
transform: translateX(0);
|
|
4359
|
+
}
|
|
4360
|
+
|
|
4361
|
+
.sf-app-shell[data-mobile="drawer"][data-sidebar-open="true"] .sf-app-backdrop {
|
|
4362
|
+
background: var(--color-backdrop);
|
|
4363
|
+
display: block;
|
|
4364
|
+
inset: 0;
|
|
4365
|
+
position: fixed;
|
|
4366
|
+
z-index: 25;
|
|
4367
|
+
}
|
|
4368
|
+
}
|
|
4369
|
+
|
|
4127
4370
|
.sf-grid {
|
|
4128
4371
|
display: grid;
|
|
4129
4372
|
gap: var(--grid-gutter);
|
|
@@ -4532,6 +4775,76 @@ function buildComponentCss() {
|
|
|
4532
4775
|
--data-table-padding-inline: var(--space-xs);
|
|
4533
4776
|
}
|
|
4534
4777
|
|
|
4778
|
+
.sf-data-table :where(th button, .sf-table-sort) {
|
|
4779
|
+
align-items: center;
|
|
4780
|
+
color: inherit;
|
|
4781
|
+
display: inline-flex;
|
|
4782
|
+
font: inherit;
|
|
4783
|
+
font-weight: inherit;
|
|
4784
|
+
gap: var(--space-3xs);
|
|
4785
|
+
padding: 0;
|
|
4786
|
+
}
|
|
4787
|
+
|
|
4788
|
+
.sf-data-table :where(th button, .sf-table-sort)[aria-sort]::after {
|
|
4789
|
+
color: var(--color-muted-foreground);
|
|
4790
|
+
content: "\\2195";
|
|
4791
|
+
font-size: var(--step--2);
|
|
4792
|
+
}
|
|
4793
|
+
|
|
4794
|
+
.sf-data-table :where(th button, .sf-table-sort)[aria-sort="ascending"]::after { content: "\\2191"; }
|
|
4795
|
+
.sf-data-table :where(th button, .sf-table-sort)[aria-sort="descending"]::after { content: "\\2193"; }
|
|
4796
|
+
|
|
4797
|
+
.sf-data-table tr[data-clickable="true"] {
|
|
4798
|
+
cursor: pointer;
|
|
4799
|
+
}
|
|
4800
|
+
|
|
4801
|
+
.sf-data-table tr[data-clickable="true"]:focus-within {
|
|
4802
|
+
outline: 0.125rem solid var(--color-ring);
|
|
4803
|
+
outline-offset: -0.125rem;
|
|
4804
|
+
}
|
|
4805
|
+
|
|
4806
|
+
@container (max-width: 38rem) {
|
|
4807
|
+
.sf-table-wrap[data-view="cards"] {
|
|
4808
|
+
border: 0;
|
|
4809
|
+
overflow: visible;
|
|
4810
|
+
}
|
|
4811
|
+
|
|
4812
|
+
.sf-table-wrap[data-view="cards"] .sf-data-table,
|
|
4813
|
+
.sf-table-wrap[data-view="cards"] .sf-data-table :where(thead, tbody, tr, th, td) {
|
|
4814
|
+
display: block;
|
|
4815
|
+
min-inline-size: 0;
|
|
4816
|
+
}
|
|
4817
|
+
|
|
4818
|
+
.sf-table-wrap[data-view="cards"] .sf-data-table thead {
|
|
4819
|
+
display: none;
|
|
4820
|
+
}
|
|
4821
|
+
|
|
4822
|
+
.sf-table-wrap[data-view="cards"] .sf-data-table tr {
|
|
4823
|
+
background: var(--color-card);
|
|
4824
|
+
border: 1px solid var(--color-border);
|
|
4825
|
+
border-radius: var(--radius-lg);
|
|
4826
|
+
margin-block-end: var(--space-xs);
|
|
4827
|
+
padding: var(--space-xs);
|
|
4828
|
+
}
|
|
4829
|
+
|
|
4830
|
+
.sf-table-wrap[data-view="cards"] .sf-data-table td {
|
|
4831
|
+
align-items: center;
|
|
4832
|
+
border: 0;
|
|
4833
|
+
display: flex;
|
|
4834
|
+
gap: var(--space-s);
|
|
4835
|
+
justify-content: space-between;
|
|
4836
|
+
padding-block: var(--space-2xs);
|
|
4837
|
+
padding-inline: 0;
|
|
4838
|
+
}
|
|
4839
|
+
|
|
4840
|
+
.sf-table-wrap[data-view="cards"] .sf-data-table td::before {
|
|
4841
|
+
color: var(--color-muted-foreground);
|
|
4842
|
+
content: attr(data-label);
|
|
4843
|
+
font-size: var(--step--1);
|
|
4844
|
+
font-weight: 800;
|
|
4845
|
+
}
|
|
4846
|
+
}
|
|
4847
|
+
|
|
4535
4848
|
.sf-status {
|
|
4536
4849
|
align-items: center;
|
|
4537
4850
|
background: var(--status-bg, var(--color-surface-alt));
|
|
@@ -4547,6 +4860,12 @@ function buildComponentCss() {
|
|
|
4547
4860
|
padding-inline: .7em;
|
|
4548
4861
|
}
|
|
4549
4862
|
|
|
4863
|
+
.sf-status[data-size="sm"] {
|
|
4864
|
+
font-size: var(--step--2);
|
|
4865
|
+
min-block-size: 1.35rem;
|
|
4866
|
+
padding-inline: .55em;
|
|
4867
|
+
}
|
|
4868
|
+
|
|
4550
4869
|
.sf-status::before {
|
|
4551
4870
|
background: currentColor;
|
|
4552
4871
|
block-size: .42em;
|
|
@@ -4629,6 +4948,12 @@ function buildComponentCss() {
|
|
|
4629
4948
|
gap: var(--space-2xs);
|
|
4630
4949
|
}
|
|
4631
4950
|
|
|
4951
|
+
.sf-form[data-density="compact"],
|
|
4952
|
+
.sf-field[data-density="compact"] {
|
|
4953
|
+
--input-block-size: 2.25rem;
|
|
4954
|
+
--input-padding-inline: var(--space-xs);
|
|
4955
|
+
}
|
|
4956
|
+
|
|
4632
4957
|
.sf-field > label {
|
|
4633
4958
|
color: var(--color-muted-foreground);
|
|
4634
4959
|
font-size: var(--step--1);
|
|
@@ -4641,7 +4966,41 @@ function buildComponentCss() {
|
|
|
4641
4966
|
border-radius: var(--input-radius, var(--radius-md));
|
|
4642
4967
|
color: var(--color-foreground);
|
|
4643
4968
|
min-block-size: var(--input-block-size, 2.75rem);
|
|
4644
|
-
padding-inline: var(--space-s);
|
|
4969
|
+
padding-inline: var(--input-padding-inline, var(--space-s));
|
|
4970
|
+
}
|
|
4971
|
+
|
|
4972
|
+
.sf-search {
|
|
4973
|
+
position: relative;
|
|
4974
|
+
}
|
|
4975
|
+
|
|
4976
|
+
.sf-search::before {
|
|
4977
|
+
block-size: .75em;
|
|
4978
|
+
border: 0.125em solid currentColor;
|
|
4979
|
+
border-radius: var(--radius-full);
|
|
4980
|
+
color: var(--color-muted-foreground);
|
|
4981
|
+
content: "";
|
|
4982
|
+
inline-size: .75em;
|
|
4983
|
+
inset-block-start: 50%;
|
|
4984
|
+
inset-inline-start: var(--space-xs);
|
|
4985
|
+
line-height: 1;
|
|
4986
|
+
position: absolute;
|
|
4987
|
+
transform: translateY(-58%);
|
|
4988
|
+
}
|
|
4989
|
+
|
|
4990
|
+
.sf-search::after {
|
|
4991
|
+
background: var(--color-muted-foreground);
|
|
4992
|
+
block-size: .125em;
|
|
4993
|
+
content: "";
|
|
4994
|
+
inline-size: .45em;
|
|
4995
|
+
inset-block-start: 50%;
|
|
4996
|
+
inset-inline-start: calc(var(--space-xs) + .58em);
|
|
4997
|
+
position: absolute;
|
|
4998
|
+
transform: translateY(120%) rotate(45deg);
|
|
4999
|
+
transform-origin: inline-start center;
|
|
5000
|
+
}
|
|
5001
|
+
|
|
5002
|
+
.sf-search > .sf-input {
|
|
5003
|
+
padding-inline-start: var(--space-l);
|
|
4645
5004
|
}
|
|
4646
5005
|
|
|
4647
5006
|
.sf-input:focus-visible {
|