@syncedco/flow 0.1.3 → 0.2.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/bin/synced-flow.mjs +369 -15
- package/components.css +175 -0
- package/docs/api-contract.md +1 -1
- package/docs/cli-reference.md +5 -0
- package/docs/native-components.md +20 -0
- package/docs/patterns.md +6 -0
- package/docs/recipes.md +4 -3
- package/docs/system-primitives.md +2 -0
- package/docs/wordpress.md +33 -0
- package/examples/wordpress/assets/css/synced-flow.css +2 -0
- package/examples/wordpress/parts/footer.html +2 -2
- package/examples/wordpress/parts/header.html +2 -2
- package/examples/wordpress/theme.json +53 -0
- package/package.json +1 -1
- package/src/presets.mjs +4 -0
- package/styles.css +175 -0
package/bin/synced-flow.mjs
CHANGED
|
@@ -491,6 +491,10 @@ ${includeDefaults ? '@import "@syncedco/flow/defaults.css";\n' : ''}@import "./$
|
|
|
491
491
|
)
|
|
492
492
|
}
|
|
493
493
|
|
|
494
|
+
if (preset === 'wordpress') {
|
|
495
|
+
writeProjectFile(resolve(targetCwd, 'theme.json'), buildWordPressThemeJson(), force)
|
|
496
|
+
}
|
|
497
|
+
|
|
494
498
|
if (!noScripts) addPackageScripts(targetCwd)
|
|
495
499
|
if (agentsTarget !== undefined || args.includes('--agents')) {
|
|
496
500
|
await installAgentGuidance({
|
|
@@ -1021,7 +1025,7 @@ function getTokenSummary() {
|
|
|
1021
1025
|
themePresets: presetNames.map(kebabThemeName),
|
|
1022
1026
|
starterClasses: {
|
|
1023
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'],
|
|
1024
|
-
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-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'],
|
|
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'],
|
|
1025
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'],
|
|
1026
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'],
|
|
1027
1031
|
},
|
|
@@ -1235,6 +1239,133 @@ export default function DialogExample() {
|
|
|
1235
1239
|
markup: '<article class="sf-container sf-prose sf-prose--blog">...</article>',
|
|
1236
1240
|
keywords: ['blog', 'article', 'prose', 'legal', 'docs', 'case-study', 'table', 'figure', 'seo'],
|
|
1237
1241
|
},
|
|
1242
|
+
{
|
|
1243
|
+
id: 'filter-toolbar',
|
|
1244
|
+
name: 'Filter toolbar',
|
|
1245
|
+
whenToUse: 'Search, status filters, quick actions, and exports above lists, tables, queues, directories, and admin screens.',
|
|
1246
|
+
classes: ['sf-filter-bar', 'sf-field', 'sf-label', 'sf-input', 'sf-select', 'sf-button', 'sf-button--outline'],
|
|
1247
|
+
requiresJs: false,
|
|
1248
|
+
requiresJsNotes: 'Use native form submit, GET params, or app-owned state. Synced Flow only styles the shell and controls.',
|
|
1249
|
+
a11y: ['Keep every field labelled.', 'Use button type="submit" for applied filters.', 'Announce result count in app markup when filtering changes content dynamically.'],
|
|
1250
|
+
gotchas: ['Do not hide labels without keeping them accessible.', 'Use app logic for debounced search, saved views, and live result counts.'],
|
|
1251
|
+
markup: {
|
|
1252
|
+
html: `<form class="sf-filter-bar" role="search" action="/app/customers" method="get">
|
|
1253
|
+
<label class="sf-field">
|
|
1254
|
+
<span class="sf-label">Search</span>
|
|
1255
|
+
<input class="sf-input" name="q" type="search" placeholder="Search customers">
|
|
1256
|
+
</label>
|
|
1257
|
+
<label class="sf-field">
|
|
1258
|
+
<span class="sf-label">Status</span>
|
|
1259
|
+
<select class="sf-select" name="status"><option>Any status</option><option>Healthy</option><option>Needs review</option></select>
|
|
1260
|
+
</label>
|
|
1261
|
+
<button class="sf-button" type="submit">Apply</button>
|
|
1262
|
+
<a class="sf-button sf-button--outline" href="/app/customers/export">Export</a>
|
|
1263
|
+
</form>`,
|
|
1264
|
+
},
|
|
1265
|
+
keywords: ['saas', 'filter', 'toolbar', 'search', 'status', 'table', 'list', 'admin', 'crm'],
|
|
1266
|
+
},
|
|
1267
|
+
{
|
|
1268
|
+
id: 'date-filter-form',
|
|
1269
|
+
name: 'Native date filter',
|
|
1270
|
+
whenToUse: 'Date ranges, billing periods, export windows, audit logs, appointments, and reporting filters that can use browser-native date/time controls.',
|
|
1271
|
+
classes: ['sf-filter-bar', 'sf-field', 'sf-label', 'sf-input', 'sf-select', 'sf-button', 'sf-button--outline'],
|
|
1272
|
+
requiresJs: false,
|
|
1273
|
+
requiresJsNotes: 'Native date, time, month, week, and datetime-local inputs submit through normal forms. Custom calendar widgets and availability logic belong to the app.',
|
|
1274
|
+
a11y: ['Use explicit visible labels for each date or time input.', 'Prefer separate start and end fields for ranges.', 'Use server or app validation for invalid ranges.'],
|
|
1275
|
+
gotchas: ['Native picker UI varies by browser and operating system.', 'Use a dedicated calendar library only when the product needs custom scheduling behavior.'],
|
|
1276
|
+
markup: {
|
|
1277
|
+
html: `<form class="sf-filter-bar" action="/app/reports" method="get">
|
|
1278
|
+
<label class="sf-field"><span class="sf-label">From</span><input class="sf-input" name="from" type="date"></label>
|
|
1279
|
+
<label class="sf-field"><span class="sf-label">To</span><input class="sf-input" name="to" type="date"></label>
|
|
1280
|
+
<label class="sf-field"><span class="sf-label">Period</span><select class="sf-select" name="period"><option>Custom</option><option>This month</option><option>Last month</option></select></label>
|
|
1281
|
+
<button class="sf-button" type="submit">Apply</button>
|
|
1282
|
+
<a class="sf-button sf-button--outline" href="/app/reports">Reset</a>
|
|
1283
|
+
</form>`,
|
|
1284
|
+
},
|
|
1285
|
+
keywords: ['saas', 'date', 'time', 'calendar', 'filter', 'range', 'reports', 'billing', 'native'],
|
|
1286
|
+
},
|
|
1287
|
+
{
|
|
1288
|
+
id: 'active-filter-tags',
|
|
1289
|
+
name: 'Active filter tags',
|
|
1290
|
+
whenToUse: 'Visible selected filters below a search/filter toolbar, with clear-all and individual removal actions owned by the app.',
|
|
1291
|
+
classes: ['sf-cluster', 'sf-badge', 'sf-button', 'sf-button--ghost', 'sf-button--sm', 'sf-link'],
|
|
1292
|
+
requiresJs: false,
|
|
1293
|
+
requiresJsNotes: 'Links can remove filters through URL params without JavaScript. Client-side filter state belongs to the consuming app.',
|
|
1294
|
+
a11y: ['Use clear link text that names the filter being removed.', 'Keep the active filter value visible as text.', 'Provide a clear-all action when several filters can be active.'],
|
|
1295
|
+
gotchas: ['Use buttons only when client-side state changes in place.', 'Use links for URL-driven filters so filtered views remain shareable.'],
|
|
1296
|
+
markup: {
|
|
1297
|
+
html: `<div class="sf-cluster" aria-label="Active filters">
|
|
1298
|
+
<span class="sf-badge">Status: Healthy <a class="sf-link" href="?plan=team" aria-label="Remove status filter">Remove</a></span>
|
|
1299
|
+
<span class="sf-badge">Plan: Team <a class="sf-link" href="?status=healthy" aria-label="Remove plan filter">Remove</a></span>
|
|
1300
|
+
<a class="sf-button sf-button--ghost sf-button--sm" href="/app/customers">Clear filters</a>
|
|
1301
|
+
</div>`,
|
|
1302
|
+
},
|
|
1303
|
+
keywords: ['saas', 'filter', 'filters', 'chips', 'tags', 'badge', 'selected', 'active'],
|
|
1304
|
+
},
|
|
1305
|
+
{
|
|
1306
|
+
id: 'data-table',
|
|
1307
|
+
name: 'Data table',
|
|
1308
|
+
whenToUse: 'Account lists, activity logs, invoices, users, queues, directories, and operational tables that should keep native table semantics.',
|
|
1309
|
+
classes: ['sf-table-wrap', 'sf-data-table', 'sf-status', 'sf-avatar', 'sf-link'],
|
|
1310
|
+
requiresJs: false,
|
|
1311
|
+
requiresJsNotes: 'Sorting, pagination state, and row actions belong to the consuming app.',
|
|
1312
|
+
a11y: ['Use a caption or labelled section.', 'Use real table headers.', 'Do not replace table markup with divs for tabular data.'],
|
|
1313
|
+
gotchas: ['Wrap wide tables in sf-table-wrap.', 'Use sf-status for display only; keep actual status text in the element.'],
|
|
1314
|
+
markup: {
|
|
1315
|
+
html: `<div class="sf-table-wrap">
|
|
1316
|
+
<table class="sf-data-table" aria-label="Customers">
|
|
1317
|
+
<thead><tr><th>Customer</th><th>Status</th><th>Plan</th><th>Updated</th></tr></thead>
|
|
1318
|
+
<tbody>
|
|
1319
|
+
<tr><td><a class="sf-link" href="/app/customers/northstar">Northstar Labs</a></td><td><span class="sf-status" data-tone="success">Healthy</span></td><td>Growth</td><td>Today</td></tr>
|
|
1320
|
+
<tr><td>Willow Group</td><td><span class="sf-status" data-tone="warning">Needs review</span></td><td>Operations</td><td>Yesterday</td></tr>
|
|
1321
|
+
</tbody>
|
|
1322
|
+
</table>
|
|
1323
|
+
</div>`,
|
|
1324
|
+
},
|
|
1325
|
+
keywords: ['saas', 'table', 'data', 'customers', 'accounts', 'admin', 'crm', 'status', 'list'],
|
|
1326
|
+
},
|
|
1327
|
+
{
|
|
1328
|
+
id: 'empty-state',
|
|
1329
|
+
name: 'Empty state',
|
|
1330
|
+
whenToUse: 'Empty queues, no search results, first-run screens, directories, and dashboards before data is available.',
|
|
1331
|
+
classes: ['sf-empty-state', 'sf-button', 'sf-button--outline', 'sf-text-muted'],
|
|
1332
|
+
requiresJs: false,
|
|
1333
|
+
requiresJsNotes: 'No JavaScript is required unless the consuming app opens a creation flow.',
|
|
1334
|
+
a11y: ['Use a heading that explains the missing state.', 'Keep primary action text specific.', 'Do not rely on illustration-only meaning.'],
|
|
1335
|
+
gotchas: ['Keep it short; empty states should unblock the next action.', 'Use sf-card around it only when the surrounding layout needs a card surface.'],
|
|
1336
|
+
markup: {
|
|
1337
|
+
html: `<div class="sf-empty-state">
|
|
1338
|
+
<h2 class="sf-text-h4">No matching customers</h2>
|
|
1339
|
+
<p class="sf-text-muted">Adjust the filters or add the first customer.</p>
|
|
1340
|
+
<div class="sf-cluster">
|
|
1341
|
+
<a class="sf-button" href="/app/customers/new">Add customer</a>
|
|
1342
|
+
<a class="sf-button sf-button--outline" href="/app/customers">Reset filters</a>
|
|
1343
|
+
</div>
|
|
1344
|
+
</div>`,
|
|
1345
|
+
},
|
|
1346
|
+
keywords: ['saas', 'empty', 'state', 'first-run', 'onboarding', 'no-results', 'zero'],
|
|
1347
|
+
},
|
|
1348
|
+
{
|
|
1349
|
+
id: 'settings-section',
|
|
1350
|
+
name: 'Settings section',
|
|
1351
|
+
whenToUse: 'Account, team, billing, security, notification, integration, profile, and site settings screens.',
|
|
1352
|
+
classes: ['sf-settings-section', 'sf-detail-panel', 'sf-data-list', 'sf-form', 'sf-field', 'sf-input', 'sf-check', 'sf-button'],
|
|
1353
|
+
requiresJs: false,
|
|
1354
|
+
requiresJsNotes: 'Form submission, optimistic saves, and validation belong to the consuming app.',
|
|
1355
|
+
a11y: ['Group related settings under a heading.', 'Use fieldsets when settings are tightly related.', 'Describe destructive settings before the action.'],
|
|
1356
|
+
gotchas: ['Avoid putting unrelated settings in one panel.', 'Use sf-detail-panel for read-only metadata next to editable fields.'],
|
|
1357
|
+
markup: {
|
|
1358
|
+
html: `<section class="sf-settings-section" aria-labelledby="workspace-settings-title">
|
|
1359
|
+
<header class="sf-repel"><div><h2 id="workspace-settings-title">Workspace settings</h2><p class="sf-text-muted">Manage visible account details.</p></div><button class="sf-button" type="submit" form="workspace-settings">Save</button></header>
|
|
1360
|
+
<form class="sf-form" id="workspace-settings">
|
|
1361
|
+
<label class="sf-field"><span class="sf-label">Workspace name</span><input class="sf-input" name="name" value="Product Suite"></label>
|
|
1362
|
+
<label class="sf-check"><input type="checkbox" name="weekly_summary" checked> <span>Email weekly summary</span></label>
|
|
1363
|
+
</form>
|
|
1364
|
+
<aside class="sf-detail-panel"><dl class="sf-data-list"><div><dt>Plan</dt><dd>Team</dd></div><div><dt>Seats</dt><dd>12 active</dd></div></dl></aside>
|
|
1365
|
+
</section>`,
|
|
1366
|
+
},
|
|
1367
|
+
keywords: ['saas', 'settings', 'section', 'billing', 'team', 'security', 'account', 'detail'],
|
|
1368
|
+
},
|
|
1238
1369
|
{
|
|
1239
1370
|
id: 'contact-form',
|
|
1240
1371
|
name: 'Accessible contact form',
|
|
@@ -1358,8 +1489,8 @@ function getPublicRecipes() {
|
|
|
1358
1489
|
id: 'saas-dashboard',
|
|
1359
1490
|
name: 'SaaS dashboard',
|
|
1360
1491
|
whenToUse: 'Authenticated SaaS apps, admin panels, customer portals, CRMs, analytics dashboards, and product workspaces.',
|
|
1361
|
-
sections: ['sticky-navigation', 'mobile-nav-drawer', 'card-grid', 'contact-form', 'long-form-content'],
|
|
1362
|
-
classes: ['sf-skip-link', 'sf-theme-light', 'sf-app-shell', 'sf-app-sidebar', 'sf-app-sidebar__brand', 'sf-app-main', 'sf-toolbar', 'sf-stack', 'sf-stack--tight', 'sf-repel', 'sf-cluster', 'sf-panel-grid', 'sf-metric-grid', 'sf-nav', 'sf-nav__list', 'sf-nav__link', 'sf-icon', 'sf-card', 'sf-surface', 'sf-stat', '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-button', 'sf-button--outline', 'sf-button--icon', 'sf-form', 'sf-field', 'sf-input', 'sf-check', 'sf-table-wrap', 'sf-font-sans', 'sf-visually-hidden'],
|
|
1492
|
+
sections: ['sticky-navigation', 'mobile-nav-drawer', 'card-grid', 'contact-form', 'long-form-content', 'filter-toolbar', 'date-filter-form', 'active-filter-tags', 'data-table', 'empty-state', 'settings-section'],
|
|
1493
|
+
classes: ['sf-skip-link', 'sf-theme-light', 'sf-app-shell', 'sf-app-sidebar', 'sf-app-sidebar__brand', 'sf-app-main', 'sf-toolbar', 'sf-stack', 'sf-stack--tight', 'sf-repel', 'sf-cluster', 'sf-panel-grid', 'sf-metric-grid', 'sf-nav', 'sf-nav__list', 'sf-nav__link', 'sf-icon', 'sf-card', 'sf-surface', 'sf-stat', '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-button', 'sf-button--outline', 'sf-button--icon', 'sf-form', 'sf-field', 'sf-input', 'sf-select', 'sf-check', 'sf-table-wrap', 'sf-font-sans', 'sf-visually-hidden'],
|
|
1363
1494
|
keywords: ['saas', 'dashboard', 'admin', 'panel', 'portal', 'crm', 'analytics', 'app', 'auth', 'login', 'signin', 'sign-in', 'user', 'account', 'metrics', 'pipeline', 'activity', 'customers', 'accounts', 'tasks', 'tickets', 'revenue'],
|
|
1364
1495
|
markup: `<a class="sf-skip-link" href="#dashboard-main">Skip to dashboard</a>
|
|
1365
1496
|
<div class="sf-app-shell sf-theme-light">
|
|
@@ -1456,38 +1587,47 @@ function getPublicRecipes() {
|
|
|
1456
1587
|
<label class="sf-meter-row"><span class="sf-repel"><strong>Accounts</strong><span class="sf-text-muted">82%</span></span><meter class="sf-meter" min="0" max="100" value="82">82%</meter></label>
|
|
1457
1588
|
<label class="sf-meter-row"><span class="sf-repel"><strong>Automation</strong><span class="sf-text-muted">64%</span></span><meter class="sf-meter" min="0" max="100" value="64">64%</meter></label>
|
|
1458
1589
|
<label class="sf-meter-row"><span class="sf-repel"><strong>Support</strong><span class="sf-text-muted">48%</span></span><meter class="sf-meter" min="0" max="100" value="48">48%</meter></label>
|
|
1590
|
+
<label class="sf-meter-row"><span class="sf-repel"><strong>Import job</strong><span class="sf-text-muted">36%</span></span><progress class="sf-progress" max="100" value="36">36%</progress></label>
|
|
1459
1591
|
</div>
|
|
1460
1592
|
</article>
|
|
1461
1593
|
</section>
|
|
1462
1594
|
<section class="sf-metric-grid" id="command">
|
|
1463
1595
|
<article class="sf-card sf-stack"><div class="sf-repel"><h2 class="sf-card__title sf-font-sans">Command centre</h2><span class="sf-badge">Priority</span></div><p class="sf-card__description">Follow-ups, approvals, and missing next actions stay visible.</p></article>
|
|
1464
1596
|
<article class="sf-card sf-stack" id="billing"><h2 class="sf-card__title sf-font-sans">Billing watch</h2><p class="sf-card__description">2 overdue payment follow-ups and 1 renewal due this week.</p></article>
|
|
1465
|
-
<article class="sf-card sf-stack"><h2 class="sf-card__title sf-font-sans">Relationship pulse</h2><p class="sf-card__description">Three customer timelines need a recent touch point.</p></article>
|
|
1597
|
+
<article class="sf-card sf-stack"><h2 class="sf-card__title sf-font-sans">Relationship pulse</h2><p class="sf-card__description">Three customer timelines need a recent touch point.</p><div class="sf-empty-state"><strong>No urgent escalations</strong><span class="sf-text-muted">New priority work will appear here.</span></div></article>
|
|
1466
1598
|
</section>
|
|
1467
1599
|
<section class="sf-panel-grid" id="customers">
|
|
1468
1600
|
<article class="sf-card sf-stack">
|
|
1469
1601
|
<div class="sf-repel"><h2 class="sf-card__title sf-font-sans">Recent activity</h2><a class="sf-link" href="/app/activity">View all</a></div>
|
|
1602
|
+
<form class="sf-filter-bar" role="search" action="/app/activity" method="get">
|
|
1603
|
+
<label class="sf-field"><span class="sf-label">Search</span><input class="sf-input" name="q" type="search" placeholder="Search activity"></label>
|
|
1604
|
+
<label class="sf-field"><span class="sf-label">Status</span><select class="sf-select" name="status"><option>Any status</option><option>Active</option><option>Scheduled</option><option>Complete</option></select></label>
|
|
1605
|
+
<label class="sf-field"><span class="sf-label">From</span><input class="sf-input" name="from" type="date"></label>
|
|
1606
|
+
<button class="sf-button" type="submit">Apply</button>
|
|
1607
|
+
</form>
|
|
1608
|
+
<div class="sf-cluster" aria-label="Active filters"><span class="sf-badge">Status: Active <a class="sf-link" href="/app/activity" aria-label="Remove status filter">Remove</a></span><a class="sf-button sf-button--ghost sf-button--sm" href="/app/activity">Clear filters</a></div>
|
|
1470
1609
|
<div class="sf-table-wrap">
|
|
1471
|
-
<table>
|
|
1610
|
+
<table class="sf-data-table" aria-label="Recent activity">
|
|
1472
1611
|
<thead><tr><th>Item</th><th>Status</th><th>Value</th><th>Date</th></tr></thead>
|
|
1473
1612
|
<tbody>
|
|
1474
|
-
<tr><td>Enterprise onboarding</td><td><span class="sf-
|
|
1475
|
-
<tr><td>Usage review</td><td><span class="sf-
|
|
1476
|
-
<tr><td>Renewal workflow</td><td><span class="sf-
|
|
1613
|
+
<tr><td>Enterprise onboarding</td><td><span class="sf-status" data-tone="success">Active</span></td><td>£7,800</td><td>Today</td></tr>
|
|
1614
|
+
<tr><td>Usage review</td><td><span class="sf-status" data-tone="info">Scheduled</span></td><td>£4,050</td><td>Yesterday</td></tr>
|
|
1615
|
+
<tr><td>Renewal workflow</td><td><span class="sf-status" data-tone="warning">Review</span></td><td>£3,250</td><td>17 May</td></tr>
|
|
1477
1616
|
</tbody>
|
|
1478
1617
|
</table>
|
|
1479
1618
|
</div>
|
|
1480
1619
|
</article>
|
|
1481
|
-
<article class="sf-card sf-stack" id="activity">
|
|
1620
|
+
<article class="sf-card sf-stack sf-detail-panel" id="activity">
|
|
1482
1621
|
<div class="sf-repel"><h2 class="sf-card__title sf-font-sans">Recent customers</h2><a class="sf-link" href="/app/customers">View all</a></div>
|
|
1483
|
-
<
|
|
1484
|
-
|
|
1485
|
-
<li
|
|
1486
|
-
<li
|
|
1622
|
+
<div class="sf-skeleton" aria-hidden="true">Loading customer details</div>
|
|
1623
|
+
<ul class="sf-list-reset sf-data-list">
|
|
1624
|
+
<li><span><strong>Northstar Labs</strong><br><span class="sf-text-muted">Growth workspace</span></span><span class="sf-status" data-tone="success">Healthy</span></li>
|
|
1625
|
+
<li><span><strong>Willow Group</strong><br><span class="sf-text-muted">Operations workspace</span></span><span class="sf-status" data-tone="warning">Needs review</span></li>
|
|
1626
|
+
<li><span><strong>Harbour Systems</strong><br><span class="sf-text-muted">Platform workspace</span></span><span class="sf-status" data-tone="success">Healthy</span></li>
|
|
1487
1627
|
</ul>
|
|
1488
1628
|
</article>
|
|
1489
1629
|
</section>
|
|
1490
|
-
<section class="sf-card sf-form" aria-labelledby="signin-title">
|
|
1630
|
+
<section class="sf-card sf-form sf-settings-section" aria-labelledby="signin-title">
|
|
1491
1631
|
<!-- Example unauthenticated state. Wire this form to your app auth provider. -->
|
|
1492
1632
|
<div class="sf-repel">
|
|
1493
1633
|
<div><p class="sf-kicker">Signed out state</p><h2 class="sf-card__title sf-font-sans" id="signin-title">Sign in to your workspace</h2></div>
|
|
@@ -1680,7 +1820,7 @@ function runTokens() {
|
|
|
1680
1820
|
function runCatalog() {
|
|
1681
1821
|
const catalog = getPublicCatalog()
|
|
1682
1822
|
if (args.includes('--json')) {
|
|
1683
|
-
|
|
1823
|
+
writeFileSync(1, `${JSON.stringify(catalog, null, 2)}\n`)
|
|
1684
1824
|
return
|
|
1685
1825
|
}
|
|
1686
1826
|
|
|
@@ -2426,6 +2566,45 @@ function formatStringArray(values) {
|
|
|
2426
2566
|
return `[${values.map((value) => `'${value}'`).join(', ')}]`
|
|
2427
2567
|
}
|
|
2428
2568
|
|
|
2569
|
+
function buildWordPressThemeJson() {
|
|
2570
|
+
return `${JSON.stringify({
|
|
2571
|
+
$schema: 'https://schemas.wp.org/trunk/theme.json',
|
|
2572
|
+
version: 3,
|
|
2573
|
+
settings: {
|
|
2574
|
+
appearanceTools: true,
|
|
2575
|
+
color: {
|
|
2576
|
+
palette: [
|
|
2577
|
+
{ slug: 'base', name: 'Base', color: 'var(--sf-colour-background)' },
|
|
2578
|
+
{ slug: 'foreground', name: 'Foreground', color: 'var(--sf-colour-foreground)' },
|
|
2579
|
+
{ slug: 'muted', name: 'Muted', color: 'var(--sf-colour-muted)' },
|
|
2580
|
+
{ slug: 'surface', name: 'Surface', color: 'var(--sf-colour-surface)' },
|
|
2581
|
+
{ slug: 'surface-alt', name: 'Surface Alt', color: 'var(--sf-colour-surface-alt)' },
|
|
2582
|
+
{ slug: 'surface-elevated', name: 'Surface Elevated', color: 'var(--sf-colour-surface-elevated, var(--sf-colour-surface-raised))' },
|
|
2583
|
+
{ slug: 'border', name: 'Border', color: 'var(--sf-colour-border)' },
|
|
2584
|
+
{ slug: 'primary', name: 'Primary', color: 'var(--sf-colour-primary)' },
|
|
2585
|
+
{ slug: 'primary-foreground', name: 'Primary Foreground', color: 'var(--sf-colour-primary-foreground)' },
|
|
2586
|
+
],
|
|
2587
|
+
},
|
|
2588
|
+
layout: {
|
|
2589
|
+
contentSize: '72rem',
|
|
2590
|
+
wideSize: '90rem',
|
|
2591
|
+
},
|
|
2592
|
+
},
|
|
2593
|
+
styles: {
|
|
2594
|
+
color: {
|
|
2595
|
+
background: 'var(--sf-colour-background)',
|
|
2596
|
+
text: 'var(--sf-colour-foreground)',
|
|
2597
|
+
},
|
|
2598
|
+
spacing: {
|
|
2599
|
+
blockGap: 'var(--space-s-l)',
|
|
2600
|
+
},
|
|
2601
|
+
typography: {
|
|
2602
|
+
fontFamily: 'var(--sf-font-sans)',
|
|
2603
|
+
},
|
|
2604
|
+
},
|
|
2605
|
+
}, null, 2)}\n`
|
|
2606
|
+
}
|
|
2607
|
+
|
|
2429
2608
|
function projectContains(targetCwd, needle) {
|
|
2430
2609
|
return allProjectFiles(targetCwd).some((file) => readFileSync(file, 'utf8').includes(needle))
|
|
2431
2610
|
}
|
|
@@ -4270,6 +4449,181 @@ function buildComponentCss() {
|
|
|
4270
4449
|
overflow: hidden;
|
|
4271
4450
|
}
|
|
4272
4451
|
|
|
4452
|
+
.sf-progress {
|
|
4453
|
+
accent-color: var(--progress-color, var(--color-primary));
|
|
4454
|
+
block-size: var(--progress-size, .625rem);
|
|
4455
|
+
inline-size: 100%;
|
|
4456
|
+
}
|
|
4457
|
+
|
|
4458
|
+
.sf-progress::-webkit-progress-bar {
|
|
4459
|
+
background: var(--color-surface-alt);
|
|
4460
|
+
border-radius: var(--radius-full);
|
|
4461
|
+
}
|
|
4462
|
+
|
|
4463
|
+
.sf-progress::-webkit-progress-value,
|
|
4464
|
+
.sf-progress::-moz-progress-bar {
|
|
4465
|
+
background: var(--progress-color, var(--color-primary));
|
|
4466
|
+
border-radius: var(--radius-full);
|
|
4467
|
+
}
|
|
4468
|
+
|
|
4469
|
+
.sf-skeleton {
|
|
4470
|
+
background: color-mix(in oklch, var(--color-surface-alt) 82%, var(--color-foreground));
|
|
4471
|
+
border-radius: var(--skeleton-radius, var(--radius-md));
|
|
4472
|
+
color: transparent;
|
|
4473
|
+
min-block-size: var(--skeleton-block-size, 1em);
|
|
4474
|
+
}
|
|
4475
|
+
|
|
4476
|
+
@media (prefers-reduced-motion: no-preference) {
|
|
4477
|
+
.sf-skeleton {
|
|
4478
|
+
animation: sf-skeleton-pulse 1.4s var(--ease-standard) infinite alternate;
|
|
4479
|
+
}
|
|
4480
|
+
}
|
|
4481
|
+
|
|
4482
|
+
@keyframes sf-skeleton-pulse {
|
|
4483
|
+
to { opacity: .62; }
|
|
4484
|
+
}
|
|
4485
|
+
|
|
4486
|
+
.sf-filter-bar {
|
|
4487
|
+
align-items: end;
|
|
4488
|
+
background: var(--color-card);
|
|
4489
|
+
border: 1px solid var(--color-border);
|
|
4490
|
+
border-radius: var(--radius-lg);
|
|
4491
|
+
display: flex;
|
|
4492
|
+
flex-wrap: wrap;
|
|
4493
|
+
gap: var(--space-xs);
|
|
4494
|
+
padding: var(--space-s);
|
|
4495
|
+
}
|
|
4496
|
+
|
|
4497
|
+
.sf-filter-bar > :where(.sf-field, form, label) {
|
|
4498
|
+
flex: 1 1 min(100%, 14rem);
|
|
4499
|
+
}
|
|
4500
|
+
|
|
4501
|
+
.sf-filter-bar > :where(.sf-button, button, a) {
|
|
4502
|
+
flex: 0 0 auto;
|
|
4503
|
+
}
|
|
4504
|
+
|
|
4505
|
+
.sf-data-table {
|
|
4506
|
+
border-collapse: collapse;
|
|
4507
|
+
inline-size: 100%;
|
|
4508
|
+
min-inline-size: var(--data-table-min, 44rem);
|
|
4509
|
+
}
|
|
4510
|
+
|
|
4511
|
+
.sf-data-table :where(th, td) {
|
|
4512
|
+
border-block-end: 1px solid var(--color-border);
|
|
4513
|
+
padding-block: var(--data-table-padding-block, var(--space-xs));
|
|
4514
|
+
padding-inline: var(--data-table-padding-inline, var(--space-s));
|
|
4515
|
+
text-align: start;
|
|
4516
|
+
vertical-align: middle;
|
|
4517
|
+
}
|
|
4518
|
+
|
|
4519
|
+
.sf-data-table :where(th) {
|
|
4520
|
+
background: var(--color-surface-alt);
|
|
4521
|
+
color: var(--color-muted-foreground);
|
|
4522
|
+
font-size: var(--step--1);
|
|
4523
|
+
font-weight: 800;
|
|
4524
|
+
}
|
|
4525
|
+
|
|
4526
|
+
.sf-data-table :where(tbody tr:hover) {
|
|
4527
|
+
background: color-mix(in oklch, var(--color-primary) 6%, transparent);
|
|
4528
|
+
}
|
|
4529
|
+
|
|
4530
|
+
.sf-data-table[data-density="compact"] {
|
|
4531
|
+
--data-table-padding-block: var(--space-2xs);
|
|
4532
|
+
--data-table-padding-inline: var(--space-xs);
|
|
4533
|
+
}
|
|
4534
|
+
|
|
4535
|
+
.sf-status {
|
|
4536
|
+
align-items: center;
|
|
4537
|
+
background: var(--status-bg, var(--color-surface-alt));
|
|
4538
|
+
border: 1px solid var(--status-border, var(--color-border));
|
|
4539
|
+
border-radius: var(--radius-full);
|
|
4540
|
+
color: var(--status-color, var(--color-foreground));
|
|
4541
|
+
display: inline-flex;
|
|
4542
|
+
font-size: var(--step--1);
|
|
4543
|
+
font-weight: 700;
|
|
4544
|
+
gap: var(--space-3xs);
|
|
4545
|
+
line-height: 1;
|
|
4546
|
+
min-block-size: 1.75rem;
|
|
4547
|
+
padding-inline: .7em;
|
|
4548
|
+
}
|
|
4549
|
+
|
|
4550
|
+
.sf-status::before {
|
|
4551
|
+
background: currentColor;
|
|
4552
|
+
block-size: .42em;
|
|
4553
|
+
border-radius: var(--radius-full);
|
|
4554
|
+
content: "";
|
|
4555
|
+
inline-size: .42em;
|
|
4556
|
+
}
|
|
4557
|
+
|
|
4558
|
+
.sf-status[data-tone="success"] {
|
|
4559
|
+
--status-bg: var(--sf-colour-success-soft, oklch(62% 0.13 150 / 0.12));
|
|
4560
|
+
--status-border: color-mix(in oklch, var(--sf-colour-success, oklch(62% 0.13 150)) 34%, transparent);
|
|
4561
|
+
--status-color: color-mix(in oklch, var(--sf-colour-success, oklch(62% 0.13 150)) 76%, var(--color-foreground));
|
|
4562
|
+
}
|
|
4563
|
+
|
|
4564
|
+
.sf-status[data-tone="warning"] {
|
|
4565
|
+
--status-bg: var(--sf-colour-warning-soft, oklch(78% 0.15 82 / 0.16));
|
|
4566
|
+
--status-border: color-mix(in oklch, var(--sf-colour-warning, oklch(78% 0.15 82)) 38%, transparent);
|
|
4567
|
+
--status-color: color-mix(in oklch, var(--sf-colour-warning, oklch(78% 0.15 82)) 70%, var(--color-foreground));
|
|
4568
|
+
}
|
|
4569
|
+
|
|
4570
|
+
.sf-status[data-tone="danger"] {
|
|
4571
|
+
--status-bg: var(--sf-colour-danger-soft, oklch(56% 0.2 28 / 0.12));
|
|
4572
|
+
--status-border: color-mix(in oklch, var(--sf-colour-danger, var(--color-destructive)) 34%, transparent);
|
|
4573
|
+
--status-color: color-mix(in oklch, var(--sf-colour-danger, var(--color-destructive)) 76%, var(--color-foreground));
|
|
4574
|
+
}
|
|
4575
|
+
|
|
4576
|
+
.sf-status[data-tone="info"] {
|
|
4577
|
+
--status-bg: var(--sf-colour-info-soft, oklch(70% 0.12 205 / 0.14));
|
|
4578
|
+
--status-border: color-mix(in oklch, var(--sf-colour-info, oklch(70% 0.12 205)) 34%, transparent);
|
|
4579
|
+
--status-color: color-mix(in oklch, var(--sf-colour-info, oklch(70% 0.12 205)) 76%, var(--color-foreground));
|
|
4580
|
+
}
|
|
4581
|
+
|
|
4582
|
+
.sf-data-list {
|
|
4583
|
+
display: grid;
|
|
4584
|
+
}
|
|
4585
|
+
|
|
4586
|
+
.sf-data-list > * {
|
|
4587
|
+
align-items: center;
|
|
4588
|
+
border-block-end: 1px solid var(--color-border);
|
|
4589
|
+
display: flex;
|
|
4590
|
+
flex-wrap: wrap;
|
|
4591
|
+
gap: var(--space-xs);
|
|
4592
|
+
justify-content: space-between;
|
|
4593
|
+
padding-block: var(--space-xs);
|
|
4594
|
+
}
|
|
4595
|
+
|
|
4596
|
+
.sf-data-list > :last-child {
|
|
4597
|
+
border-block-end: 0;
|
|
4598
|
+
}
|
|
4599
|
+
|
|
4600
|
+
.sf-empty-state {
|
|
4601
|
+
align-items: center;
|
|
4602
|
+
background: var(--color-surface-alt);
|
|
4603
|
+
border: 1px dashed var(--color-border);
|
|
4604
|
+
border-radius: var(--radius-lg);
|
|
4605
|
+
color: var(--color-muted-foreground);
|
|
4606
|
+
display: grid;
|
|
4607
|
+
gap: var(--space-xs);
|
|
4608
|
+
justify-items: center;
|
|
4609
|
+
padding: var(--space-l);
|
|
4610
|
+
text-align: center;
|
|
4611
|
+
}
|
|
4612
|
+
|
|
4613
|
+
.sf-settings-section,
|
|
4614
|
+
.sf-detail-panel {
|
|
4615
|
+
background: var(--color-card);
|
|
4616
|
+
border: 1px solid var(--color-border);
|
|
4617
|
+
border-radius: var(--radius-lg);
|
|
4618
|
+
display: grid;
|
|
4619
|
+
gap: var(--space-s);
|
|
4620
|
+
padding: var(--space-m);
|
|
4621
|
+
}
|
|
4622
|
+
|
|
4623
|
+
.sf-detail-panel {
|
|
4624
|
+
background: var(--color-surface-alt);
|
|
4625
|
+
}
|
|
4626
|
+
|
|
4273
4627
|
.sf-field {
|
|
4274
4628
|
display: grid;
|
|
4275
4629
|
gap: var(--space-2xs);
|
package/components.css
CHANGED
|
@@ -802,6 +802,181 @@
|
|
|
802
802
|
overflow: hidden;
|
|
803
803
|
}
|
|
804
804
|
|
|
805
|
+
.sf-progress {
|
|
806
|
+
accent-color: var(--sf-progress-colour, var(--sf-colour-primary));
|
|
807
|
+
block-size: var(--sf-progress-size, .625rem);
|
|
808
|
+
inline-size: 100%;
|
|
809
|
+
}
|
|
810
|
+
|
|
811
|
+
.sf-progress::-webkit-progress-bar {
|
|
812
|
+
background: var(--sf-colour-surface-alt);
|
|
813
|
+
border-radius: var(--sf-radius-full);
|
|
814
|
+
}
|
|
815
|
+
|
|
816
|
+
.sf-progress::-webkit-progress-value,
|
|
817
|
+
.sf-progress::-moz-progress-bar {
|
|
818
|
+
background: var(--sf-progress-colour, var(--sf-colour-primary));
|
|
819
|
+
border-radius: var(--sf-radius-full);
|
|
820
|
+
}
|
|
821
|
+
|
|
822
|
+
.sf-skeleton {
|
|
823
|
+
background: color-mix(in oklch, var(--sf-colour-surface-alt) 82%, var(--sf-colour-foreground));
|
|
824
|
+
border-radius: var(--sf-skeleton-radius, var(--sf-radius-md));
|
|
825
|
+
color: transparent;
|
|
826
|
+
min-block-size: var(--sf-skeleton-block-size, 1em);
|
|
827
|
+
}
|
|
828
|
+
|
|
829
|
+
@media (prefers-reduced-motion: no-preference) {
|
|
830
|
+
.sf-skeleton {
|
|
831
|
+
animation: sf-skeleton-pulse 1.4s var(--sf-ease-standard) infinite alternate;
|
|
832
|
+
}
|
|
833
|
+
}
|
|
834
|
+
|
|
835
|
+
@keyframes sf-skeleton-pulse {
|
|
836
|
+
to { opacity: .62; }
|
|
837
|
+
}
|
|
838
|
+
|
|
839
|
+
.sf-filter-bar {
|
|
840
|
+
align-items: end;
|
|
841
|
+
background: var(--sf-colour-surface);
|
|
842
|
+
border: 1px solid var(--sf-colour-border);
|
|
843
|
+
border-radius: var(--sf-radius-panel);
|
|
844
|
+
display: flex;
|
|
845
|
+
flex-wrap: wrap;
|
|
846
|
+
gap: var(--sf-space-xs);
|
|
847
|
+
padding: var(--sf-space-s);
|
|
848
|
+
}
|
|
849
|
+
|
|
850
|
+
.sf-filter-bar > :where(.sf-field, form, label) {
|
|
851
|
+
flex: 1 1 min(100%, 14rem);
|
|
852
|
+
}
|
|
853
|
+
|
|
854
|
+
.sf-filter-bar > :where(.sf-button, button, a) {
|
|
855
|
+
flex: 0 0 auto;
|
|
856
|
+
}
|
|
857
|
+
|
|
858
|
+
.sf-data-table {
|
|
859
|
+
border-collapse: collapse;
|
|
860
|
+
inline-size: 100%;
|
|
861
|
+
min-inline-size: var(--sf-data-table-min, 44rem);
|
|
862
|
+
}
|
|
863
|
+
|
|
864
|
+
.sf-data-table :where(th, td) {
|
|
865
|
+
border-block-end: 1px solid var(--sf-colour-border);
|
|
866
|
+
padding-block: var(--sf-data-table-padding-block, var(--sf-space-xs));
|
|
867
|
+
padding-inline: var(--sf-data-table-padding-inline, var(--sf-space-s));
|
|
868
|
+
text-align: start;
|
|
869
|
+
vertical-align: middle;
|
|
870
|
+
}
|
|
871
|
+
|
|
872
|
+
.sf-data-table :where(th) {
|
|
873
|
+
background: var(--sf-colour-surface-alt);
|
|
874
|
+
color: var(--sf-colour-muted);
|
|
875
|
+
font-size: var(--sf-step--1);
|
|
876
|
+
font-weight: 800;
|
|
877
|
+
}
|
|
878
|
+
|
|
879
|
+
.sf-data-table :where(tbody tr:hover) {
|
|
880
|
+
background: color-mix(in oklch, var(--sf-colour-primary) 6%, transparent);
|
|
881
|
+
}
|
|
882
|
+
|
|
883
|
+
.sf-data-table[data-density="compact"] {
|
|
884
|
+
--sf-data-table-padding-block: var(--sf-space-2xs);
|
|
885
|
+
--sf-data-table-padding-inline: var(--sf-space-xs);
|
|
886
|
+
}
|
|
887
|
+
|
|
888
|
+
.sf-status {
|
|
889
|
+
align-items: center;
|
|
890
|
+
background: var(--sf-status-bg, var(--sf-colour-surface-alt));
|
|
891
|
+
border: 1px solid var(--sf-status-border, var(--sf-colour-border));
|
|
892
|
+
border-radius: var(--sf-radius-full);
|
|
893
|
+
color: var(--sf-status-colour, var(--sf-colour-foreground));
|
|
894
|
+
display: inline-flex;
|
|
895
|
+
font-size: var(--sf-step--1);
|
|
896
|
+
font-weight: 700;
|
|
897
|
+
gap: var(--sf-space-3xs);
|
|
898
|
+
line-height: 1;
|
|
899
|
+
min-block-size: 1.75rem;
|
|
900
|
+
padding-inline: .7em;
|
|
901
|
+
}
|
|
902
|
+
|
|
903
|
+
.sf-status::before {
|
|
904
|
+
background: currentColor;
|
|
905
|
+
block-size: .42em;
|
|
906
|
+
border-radius: var(--sf-radius-full);
|
|
907
|
+
content: "";
|
|
908
|
+
inline-size: .42em;
|
|
909
|
+
}
|
|
910
|
+
|
|
911
|
+
.sf-status[data-tone="success"] {
|
|
912
|
+
--sf-status-bg: var(--sf-colour-success-soft);
|
|
913
|
+
--sf-status-border: color-mix(in oklch, var(--sf-colour-success) 34%, transparent);
|
|
914
|
+
--sf-status-colour: color-mix(in oklch, var(--sf-colour-success) 76%, var(--sf-colour-foreground));
|
|
915
|
+
}
|
|
916
|
+
|
|
917
|
+
.sf-status[data-tone="warning"] {
|
|
918
|
+
--sf-status-bg: var(--sf-colour-warning-soft);
|
|
919
|
+
--sf-status-border: color-mix(in oklch, var(--sf-colour-warning) 38%, transparent);
|
|
920
|
+
--sf-status-colour: color-mix(in oklch, var(--sf-colour-warning) 70%, var(--sf-colour-foreground));
|
|
921
|
+
}
|
|
922
|
+
|
|
923
|
+
.sf-status[data-tone="danger"] {
|
|
924
|
+
--sf-status-bg: var(--sf-colour-danger-soft);
|
|
925
|
+
--sf-status-border: color-mix(in oklch, var(--sf-colour-danger) 34%, transparent);
|
|
926
|
+
--sf-status-colour: color-mix(in oklch, var(--sf-colour-danger) 76%, var(--sf-colour-foreground));
|
|
927
|
+
}
|
|
928
|
+
|
|
929
|
+
.sf-status[data-tone="info"] {
|
|
930
|
+
--sf-status-bg: var(--sf-colour-info-soft);
|
|
931
|
+
--sf-status-border: color-mix(in oklch, var(--sf-colour-info) 34%, transparent);
|
|
932
|
+
--sf-status-colour: color-mix(in oklch, var(--sf-colour-info) 76%, var(--sf-colour-foreground));
|
|
933
|
+
}
|
|
934
|
+
|
|
935
|
+
.sf-data-list {
|
|
936
|
+
display: grid;
|
|
937
|
+
}
|
|
938
|
+
|
|
939
|
+
.sf-data-list > * {
|
|
940
|
+
align-items: center;
|
|
941
|
+
border-block-end: 1px solid var(--sf-colour-border);
|
|
942
|
+
display: flex;
|
|
943
|
+
flex-wrap: wrap;
|
|
944
|
+
gap: var(--sf-space-xs);
|
|
945
|
+
justify-content: space-between;
|
|
946
|
+
padding-block: var(--sf-space-xs);
|
|
947
|
+
}
|
|
948
|
+
|
|
949
|
+
.sf-data-list > :last-child {
|
|
950
|
+
border-block-end: 0;
|
|
951
|
+
}
|
|
952
|
+
|
|
953
|
+
.sf-empty-state {
|
|
954
|
+
align-items: center;
|
|
955
|
+
background: var(--sf-colour-surface-alt);
|
|
956
|
+
border: 1px dashed var(--sf-colour-border-strong);
|
|
957
|
+
border-radius: var(--sf-radius-panel);
|
|
958
|
+
color: var(--sf-colour-muted);
|
|
959
|
+
display: grid;
|
|
960
|
+
gap: var(--sf-space-xs);
|
|
961
|
+
justify-items: center;
|
|
962
|
+
padding: var(--sf-space-l);
|
|
963
|
+
text-align: center;
|
|
964
|
+
}
|
|
965
|
+
|
|
966
|
+
.sf-settings-section,
|
|
967
|
+
.sf-detail-panel {
|
|
968
|
+
background: var(--sf-colour-surface);
|
|
969
|
+
border: 1px solid var(--sf-colour-border);
|
|
970
|
+
border-radius: var(--sf-radius-panel);
|
|
971
|
+
display: grid;
|
|
972
|
+
gap: var(--sf-space-s);
|
|
973
|
+
padding: var(--sf-space-m);
|
|
974
|
+
}
|
|
975
|
+
|
|
976
|
+
.sf-detail-panel {
|
|
977
|
+
background: var(--sf-colour-surface-alt);
|
|
978
|
+
}
|
|
979
|
+
|
|
805
980
|
.sf-nav {
|
|
806
981
|
align-items: center;
|
|
807
982
|
display: flex;
|
package/docs/api-contract.md
CHANGED
|
@@ -14,7 +14,7 @@ These are intended for application code and examples.
|
|
|
14
14
|
| Tokens | Unlayered `--sf-*` custom properties emitted by `tokens.css` |
|
|
15
15
|
| Theme config | `theme.fonts`, `theme.colours`, `theme.darkColours`, `theme.radii`, `theme.layout`, `theme.components` |
|
|
16
16
|
| Layout classes | `sf-container`, `sf-section`, `sf-stack`, `sf-flow`, `sf-cluster`, `sf-repel`, `sf-toolbar`, `sf-app-shell`, `sf-app-sidebar`, `sf-app-main`, `sf-auto-grid`, `sf-switcher`, `sf-sidebar`, `sf-split`, `sf-frame`, `sf-cover`, `sf-metric-grid`, `sf-pipeline` |
|
|
17
|
-
| Components | `sf-button`, `sf-icon`, `sf-icon-button`, `sf-avatar`, `sf-chart`, `sf-meter`, `sf-card`, `sf-surface`, `sf-hero`, `sf-nav`, `sf-form`, `sf-field`, `sf-input`, `sf-select`, `sf-textarea`, `sf-check`, `sf-alert`, `sf-badge`, `sf-section-header`, `sf-kicker` |
|
|
17
|
+
| Components | `sf-button`, `sf-icon`, `sf-icon-button`, `sf-avatar`, `sf-chart`, `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-card`, `sf-surface`, `sf-hero`, `sf-nav`, `sf-form`, `sf-field`, `sf-input`, `sf-select`, `sf-textarea`, `sf-check`, `sf-alert`, `sf-badge`, `sf-section-header`, `sf-kicker` |
|
|
18
18
|
| Native components | `sf-dialog`, `sf-popover`, `sf-tooltip`, `sf-drawer`, `sf-drawer--stack`, `sf-disclosure`, `sf-accordion`, `sf-tabs`, `sf-menu`, `sf-breadcrumb`, `sf-pagination` |
|
|
19
19
|
| Website patterns | `sf-logo-cloud`, `sf-feature`, `sf-stats`, `sf-testimonial`, `sf-pricing-grid`, `sf-price-card`, `sf-faq`, `sf-cta`, `sf-footer` |
|
|
20
20
|
| Utilities | `sf-prose`, `sf-link`, `sf-link-subtle`, `sf-link-plain`, `sf-list-*`, `sf-push-*`, `sf-focus-ring`, `sf-touch-target`, `sf-skip-link`, `sr-only`, `not-sr-only` |
|
package/docs/cli-reference.md
CHANGED
|
@@ -28,6 +28,11 @@ Options:
|
|
|
28
28
|
| `--no-scripts` | Do not update `package.json`. |
|
|
29
29
|
| `--force` | Overwrite init-managed files. |
|
|
30
30
|
|
|
31
|
+
The WordPress preset also writes a `theme.json` colour palette that maps
|
|
32
|
+
WordPress editor presets such as `base`, `surface`, `surface-elevated`, and
|
|
33
|
+
`primary` to Synced Flow semantic tokens. Use those presets on blocks instead of
|
|
34
|
+
overriding WordPress `.has-*` preset classes in CSS.
|
|
35
|
+
|
|
31
36
|
## agents
|
|
32
37
|
|
|
33
38
|
Install or inspect project-level AI guidance.
|
|
@@ -128,6 +128,26 @@ Use `sf-nav--mobile` with a popover drawer for mobile navigation. Use
|
|
|
128
128
|
</nav>
|
|
129
129
|
```
|
|
130
130
|
|
|
131
|
+
## Native Date And Time Inputs
|
|
132
|
+
|
|
133
|
+
Use browser-native inputs for simple date, time, month, week, and datetime
|
|
134
|
+
fields. Synced Flow styles the control shell with `sf-input`; validation,
|
|
135
|
+
range rules, and custom scheduling behavior belong to the app.
|
|
136
|
+
|
|
137
|
+
```html
|
|
138
|
+
<form class="sf-filter-bar" action="/app/reports" method="get">
|
|
139
|
+
<label class="sf-field">
|
|
140
|
+
<span class="sf-label">From</span>
|
|
141
|
+
<input class="sf-input" name="from" type="date">
|
|
142
|
+
</label>
|
|
143
|
+
<label class="sf-field">
|
|
144
|
+
<span class="sf-label">To</span>
|
|
145
|
+
<input class="sf-input" name="to" type="date">
|
|
146
|
+
</label>
|
|
147
|
+
<button class="sf-button" type="submit">Apply</button>
|
|
148
|
+
</form>
|
|
149
|
+
```
|
|
150
|
+
|
|
131
151
|
## Scroll And Sticky
|
|
132
152
|
|
|
133
153
|
Use CSS scroll snap and sticky positioning for full-page sections and sticky
|
package/docs/patterns.md
CHANGED
|
@@ -18,6 +18,12 @@ pnpm exec synced-flow pattern scroll-viewport-sections --json
|
|
|
18
18
|
| `scroll-viewport-with-spy` | Scroll snap panels with optional IntersectionObserver active-state notes. |
|
|
19
19
|
| `native-dialog-react` | Native `<dialog>` markup plus React/Next fallback shape. |
|
|
20
20
|
| `popover-drawer-layout` | Popover-backed drawer or filter panel with `sf-drawer--stack`. |
|
|
21
|
+
| `filter-toolbar` | Search, filters, and actions above app lists and tables. |
|
|
22
|
+
| `date-filter-form` | Native date/time inputs inside filter forms. |
|
|
23
|
+
| `active-filter-tags` | Visible selected filter tags with remove and clear actions. |
|
|
24
|
+
| `data-table` | Native responsive table markup with status pills. |
|
|
25
|
+
| `empty-state` | No-results and first-run states with clear next actions. |
|
|
26
|
+
| `settings-section` | Settings/detail sections for account, team, billing, and security screens. |
|
|
21
27
|
|
|
22
28
|
Pattern JSON includes:
|
|
23
29
|
|
package/docs/recipes.md
CHANGED
|
@@ -160,12 +160,13 @@ The recipe includes:
|
|
|
160
160
|
- Generic account initials using `sf-avatar`
|
|
161
161
|
- Inline SVG and native meter analytics using `sf-chart` and `sf-meter`
|
|
162
162
|
- traditional SaaS app shell with persistent desktop navigation and a visible stacked mobile navigation rail
|
|
163
|
-
- KPI cards, theme-aware toolbar, filter/search form,
|
|
163
|
+
- KPI cards, theme-aware toolbar, filter/search form, native date input, active filter tags, data table, status pills, progress, skeleton loading, empty state, and activity list
|
|
164
164
|
|
|
165
165
|
It uses Synced Flow primitives such as `sf-app-shell`, `sf-app-sidebar`,
|
|
166
166
|
`sf-app-main`, `sf-toolbar`, `sf-metric-grid`, `sf-panel-grid`, `sf-icon`,
|
|
167
|
-
`sf-chart`, `sf-chart--wide`, `sf-meter`, `sf-
|
|
168
|
-
`sf-table-
|
|
167
|
+
`sf-chart`, `sf-chart--wide`, `sf-meter`, `sf-progress`, `sf-skeleton`,
|
|
168
|
+
`sf-card`, `sf-form`, `sf-filter-bar`, `sf-data-table`, `sf-status`,
|
|
169
|
+
`sf-data-list`, `sf-empty-state`, `sf-detail-panel`, and `sf-table-wrap`.
|
|
169
170
|
|
|
170
171
|
## Documentation Page
|
|
171
172
|
|
|
@@ -70,6 +70,8 @@ These cover the common UI elements needed for a simple site.
|
|
|
70
70
|
| `sf-alert`, `sf-alert--info`, `sf-alert--success`, `sf-alert--warning`, `sf-alert--danger`, `sf-alert__title` | Notices and feedback. |
|
|
71
71
|
| `sf-section-header`, `sf-kicker`, `sf-badge`, `sf-avatar` | Common marketing/content and account patterns. |
|
|
72
72
|
| `sf-chart`, `sf-chart__plot`, `sf-chart__svg`, `sf-chart__legend`, `sf-meter-list`, `sf-meter` | Lightweight chart shells for app-owned SVG charts and native meter bars. |
|
|
73
|
+
| `sf-progress`, `sf-skeleton` | Native progress bars and CSS-only loading placeholders for async app states. |
|
|
74
|
+
| `sf-filter-bar`, `sf-data-table`, `sf-status`, `sf-data-list`, `sf-empty-state`, `sf-settings-section`, `sf-detail-panel` | Lean application primitives for filters, operational data, status, empty states, and settings/detail screens. |
|
|
73
75
|
|
|
74
76
|
## Utility Classes
|
|
75
77
|
|
package/docs/wordpress.md
CHANGED
|
@@ -18,6 +18,8 @@ The WordPress preset:
|
|
|
18
18
|
- scans PHP, HTML, JS, TS, JSX, TSX, Twig, Vue, Svelte, Astro, and MDX files
|
|
19
19
|
- enables `includeCore`
|
|
20
20
|
- writes `assets/css/synced-flow.css`
|
|
21
|
+
- writes a `theme.json` palette that maps WordPress colour presets to Synced
|
|
22
|
+
Flow semantic tokens
|
|
21
23
|
|
|
22
24
|
See [`examples/wordpress`](../examples/wordpress) for a lean block theme style
|
|
23
25
|
template with `theme.json`, template parts, PHP patterns, enqueue code, and an
|
|
@@ -54,6 +56,37 @@ wp_enqueue_style(
|
|
|
54
56
|
For plugins, use `plugins_url()` or `plugin_dir_url()` instead of
|
|
55
57
|
`get_theme_file_uri()`.
|
|
56
58
|
|
|
59
|
+
## WordPress Presets
|
|
60
|
+
|
|
61
|
+
WordPress emits editor preset classes such as `.has-base-background-color`,
|
|
62
|
+
`.has-primary-color`, and `.has-large-font-size` with `!important`. Treat those
|
|
63
|
+
classes as the editor's chosen design value.
|
|
64
|
+
|
|
65
|
+
Do not override `.has-*` preset classes in project CSS. Add or choose a better
|
|
66
|
+
semantic preset instead, then use that preset on the block.
|
|
67
|
+
|
|
68
|
+
The generated `theme.json` maps common WordPress colour presets to Synced Flow
|
|
69
|
+
tokens:
|
|
70
|
+
|
|
71
|
+
```json
|
|
72
|
+
{
|
|
73
|
+
"slug": "surface-elevated",
|
|
74
|
+
"name": "Surface Elevated",
|
|
75
|
+
"color": "var(--sf-colour-surface-elevated, var(--sf-colour-surface-raised))"
|
|
76
|
+
}
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
Use those presets for any block that needs an editor-visible design role:
|
|
80
|
+
|
|
81
|
+
- `base` for page backgrounds
|
|
82
|
+
- `surface` and `surface-alt` for standard panels
|
|
83
|
+
- `surface-elevated` for chrome, mastheads, footers, sticky bars, and raised
|
|
84
|
+
regions
|
|
85
|
+
- `primary` and `primary-foreground` for branded actions
|
|
86
|
+
|
|
87
|
+
If a new visual role appears repeatedly, add it to `synced-flow.config.mjs` and
|
|
88
|
+
expose it in `theme.json`; avoid adding `!important` overrides.
|
|
89
|
+
|
|
57
90
|
## Dynamic Classes
|
|
58
91
|
|
|
59
92
|
Synced Flow generates utility CSS from complete class tokens. Keep class names
|
|
@@ -168,6 +168,8 @@
|
|
|
168
168
|
--color-surface: var(--sf-colour-surface);
|
|
169
169
|
--sf-colour-surface-alt: oklch(96.4% 0.011 80);
|
|
170
170
|
--color-surface-alt: var(--sf-colour-surface-alt);
|
|
171
|
+
--sf-colour-surface-elevated: var(--sf-colour-surface-raised);
|
|
172
|
+
--color-surface-elevated: var(--sf-colour-surface-elevated);
|
|
171
173
|
--sf-colour-border: oklch(18% 0.026 250 / 0.12);
|
|
172
174
|
--color-border: var(--sf-colour-border);
|
|
173
175
|
--sf-colour-primary: oklch(68% 0.18 44);
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
<!-- wp:group {"tagName":"footer","className":"sf-footer"} -->
|
|
2
|
-
<footer class="wp-block-group sf-footer">
|
|
1
|
+
<!-- wp:group {"tagName":"footer","backgroundColor":"surface-elevated","className":"sf-footer"} -->
|
|
2
|
+
<footer class="wp-block-group sf-footer has-surface-elevated-background-color has-background">
|
|
3
3
|
<!-- wp:group {"className":"sf-container sf-repel"} -->
|
|
4
4
|
<div class="wp-block-group sf-container sf-repel">
|
|
5
5
|
<!-- wp:paragraph {"className":"sf-text-muted"} -->
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
<!-- wp:group {"tagName":"header","className":"sf-section sf-section--compact sf-sticky-top
|
|
2
|
-
<header class="wp-block-group sf-section sf-section--compact sf-sticky-top
|
|
1
|
+
<!-- wp:group {"tagName":"header","backgroundColor":"surface-elevated","className":"sf-section sf-section--compact sf-sticky-top"} -->
|
|
2
|
+
<header class="wp-block-group sf-section sf-section--compact sf-sticky-top has-surface-elevated-background-color has-background">
|
|
3
3
|
<!-- wp:group {"className":"sf-container sf-nav","layout":{"type":"flex","justifyContent":"space-between"}} -->
|
|
4
4
|
<div class="wp-block-group sf-container sf-nav">
|
|
5
5
|
<!-- wp:site-title {"className":"sf-nav__link"} /-->
|
|
@@ -3,12 +3,65 @@
|
|
|
3
3
|
"version": 3,
|
|
4
4
|
"settings": {
|
|
5
5
|
"appearanceTools": true,
|
|
6
|
+
"color": {
|
|
7
|
+
"palette": [
|
|
8
|
+
{
|
|
9
|
+
"slug": "base",
|
|
10
|
+
"name": "Base",
|
|
11
|
+
"color": "var(--sf-colour-background)"
|
|
12
|
+
},
|
|
13
|
+
{
|
|
14
|
+
"slug": "foreground",
|
|
15
|
+
"name": "Foreground",
|
|
16
|
+
"color": "var(--sf-colour-foreground)"
|
|
17
|
+
},
|
|
18
|
+
{
|
|
19
|
+
"slug": "muted",
|
|
20
|
+
"name": "Muted",
|
|
21
|
+
"color": "var(--sf-colour-muted)"
|
|
22
|
+
},
|
|
23
|
+
{
|
|
24
|
+
"slug": "surface",
|
|
25
|
+
"name": "Surface",
|
|
26
|
+
"color": "var(--sf-colour-surface)"
|
|
27
|
+
},
|
|
28
|
+
{
|
|
29
|
+
"slug": "surface-alt",
|
|
30
|
+
"name": "Surface Alt",
|
|
31
|
+
"color": "var(--sf-colour-surface-alt)"
|
|
32
|
+
},
|
|
33
|
+
{
|
|
34
|
+
"slug": "surface-elevated",
|
|
35
|
+
"name": "Surface Elevated",
|
|
36
|
+
"color": "var(--sf-colour-surface-elevated, var(--sf-colour-surface-raised))"
|
|
37
|
+
},
|
|
38
|
+
{
|
|
39
|
+
"slug": "border",
|
|
40
|
+
"name": "Border",
|
|
41
|
+
"color": "var(--sf-colour-border)"
|
|
42
|
+
},
|
|
43
|
+
{
|
|
44
|
+
"slug": "primary",
|
|
45
|
+
"name": "Primary",
|
|
46
|
+
"color": "var(--sf-colour-primary)"
|
|
47
|
+
},
|
|
48
|
+
{
|
|
49
|
+
"slug": "primary-foreground",
|
|
50
|
+
"name": "Primary Foreground",
|
|
51
|
+
"color": "var(--sf-colour-primary-foreground)"
|
|
52
|
+
}
|
|
53
|
+
]
|
|
54
|
+
},
|
|
6
55
|
"layout": {
|
|
7
56
|
"contentSize": "72rem",
|
|
8
57
|
"wideSize": "90rem"
|
|
9
58
|
}
|
|
10
59
|
},
|
|
11
60
|
"styles": {
|
|
61
|
+
"color": {
|
|
62
|
+
"background": "var(--sf-colour-background)",
|
|
63
|
+
"text": "var(--sf-colour-foreground)"
|
|
64
|
+
},
|
|
12
65
|
"spacing": {
|
|
13
66
|
"blockGap": "var(--space-s-l)"
|
|
14
67
|
},
|
package/package.json
CHANGED
package/src/presets.mjs
CHANGED
|
@@ -10,6 +10,7 @@ export const themePresets = {
|
|
|
10
10
|
muted: 'oklch(45% 0.018 236)',
|
|
11
11
|
surface: 'oklch(100% 0 0)',
|
|
12
12
|
surfaceAlt: 'oklch(96.4% 0.011 80)',
|
|
13
|
+
surfaceElevated: 'var(--sf-colour-surface-raised)',
|
|
13
14
|
border: 'oklch(18% 0.026 250 / 0.12)',
|
|
14
15
|
primary: 'oklch(68% 0.18 44)',
|
|
15
16
|
primaryHover: 'oklch(60% 0.17 42)',
|
|
@@ -46,6 +47,7 @@ export const themePresets = {
|
|
|
46
47
|
muted: 'oklch(44.6% 0.043 257)',
|
|
47
48
|
surface: 'oklch(100% 0 0)',
|
|
48
49
|
surfaceAlt: 'oklch(96.8% 0.007 247)',
|
|
50
|
+
surfaceElevated: 'var(--sf-colour-surface-raised)',
|
|
49
51
|
border: 'oklch(20.8% 0.042 265 / 0.12)',
|
|
50
52
|
primary: 'oklch(52% 0.14 250)',
|
|
51
53
|
primaryHover: 'oklch(44% 0.13 250)',
|
|
@@ -84,6 +86,7 @@ export const themePresets = {
|
|
|
84
86
|
muted: 'oklch(43% 0.02 65)',
|
|
85
87
|
surface: 'oklch(100% 0 0)',
|
|
86
88
|
surfaceAlt: 'oklch(94% 0.02 80)',
|
|
89
|
+
surfaceElevated: 'var(--sf-colour-surface-raised)',
|
|
87
90
|
border: 'oklch(18% 0.022 55 / 0.14)',
|
|
88
91
|
primary: 'oklch(58% 0.17 42)',
|
|
89
92
|
primaryHover: 'oklch(50% 0.16 40)',
|
|
@@ -122,6 +125,7 @@ export const themePresets = {
|
|
|
122
125
|
muted: 'oklch(78% 0.012 86)',
|
|
123
126
|
surface: 'oklch(18% 0.026 250)',
|
|
124
127
|
surfaceAlt: 'oklch(22% 0.024 248)',
|
|
128
|
+
surfaceElevated: 'var(--sf-colour-surface-raised)',
|
|
125
129
|
border: 'oklch(100% 0 0 / 0.14)',
|
|
126
130
|
primary: 'oklch(70% 0.12 205)',
|
|
127
131
|
primaryHover: 'oklch(76% 0.12 205)',
|
package/styles.css
CHANGED
|
@@ -1468,6 +1468,181 @@
|
|
|
1468
1468
|
overflow: hidden;
|
|
1469
1469
|
}
|
|
1470
1470
|
|
|
1471
|
+
.sf-progress {
|
|
1472
|
+
accent-color: var(--sf-progress-colour, var(--sf-colour-primary));
|
|
1473
|
+
block-size: var(--sf-progress-size, .625rem);
|
|
1474
|
+
inline-size: 100%;
|
|
1475
|
+
}
|
|
1476
|
+
|
|
1477
|
+
.sf-progress::-webkit-progress-bar {
|
|
1478
|
+
background: var(--sf-colour-surface-alt);
|
|
1479
|
+
border-radius: var(--sf-radius-full);
|
|
1480
|
+
}
|
|
1481
|
+
|
|
1482
|
+
.sf-progress::-webkit-progress-value,
|
|
1483
|
+
.sf-progress::-moz-progress-bar {
|
|
1484
|
+
background: var(--sf-progress-colour, var(--sf-colour-primary));
|
|
1485
|
+
border-radius: var(--sf-radius-full);
|
|
1486
|
+
}
|
|
1487
|
+
|
|
1488
|
+
.sf-skeleton {
|
|
1489
|
+
background: color-mix(in oklch, var(--sf-colour-surface-alt) 82%, var(--sf-colour-foreground));
|
|
1490
|
+
border-radius: var(--sf-skeleton-radius, var(--sf-radius-md));
|
|
1491
|
+
color: transparent;
|
|
1492
|
+
min-block-size: var(--sf-skeleton-block-size, 1em);
|
|
1493
|
+
}
|
|
1494
|
+
|
|
1495
|
+
@media (prefers-reduced-motion: no-preference) {
|
|
1496
|
+
.sf-skeleton {
|
|
1497
|
+
animation: sf-skeleton-pulse 1.4s var(--sf-ease-standard) infinite alternate;
|
|
1498
|
+
}
|
|
1499
|
+
}
|
|
1500
|
+
|
|
1501
|
+
@keyframes sf-skeleton-pulse {
|
|
1502
|
+
to { opacity: .62; }
|
|
1503
|
+
}
|
|
1504
|
+
|
|
1505
|
+
.sf-filter-bar {
|
|
1506
|
+
align-items: end;
|
|
1507
|
+
background: var(--sf-colour-surface);
|
|
1508
|
+
border: 1px solid var(--sf-colour-border);
|
|
1509
|
+
border-radius: var(--sf-radius-panel);
|
|
1510
|
+
display: flex;
|
|
1511
|
+
flex-wrap: wrap;
|
|
1512
|
+
gap: var(--sf-space-xs);
|
|
1513
|
+
padding: var(--sf-space-s);
|
|
1514
|
+
}
|
|
1515
|
+
|
|
1516
|
+
.sf-filter-bar > :where(.sf-field, form, label) {
|
|
1517
|
+
flex: 1 1 min(100%, 14rem);
|
|
1518
|
+
}
|
|
1519
|
+
|
|
1520
|
+
.sf-filter-bar > :where(.sf-button, button, a) {
|
|
1521
|
+
flex: 0 0 auto;
|
|
1522
|
+
}
|
|
1523
|
+
|
|
1524
|
+
.sf-data-table {
|
|
1525
|
+
border-collapse: collapse;
|
|
1526
|
+
inline-size: 100%;
|
|
1527
|
+
min-inline-size: var(--sf-data-table-min, 44rem);
|
|
1528
|
+
}
|
|
1529
|
+
|
|
1530
|
+
.sf-data-table :where(th, td) {
|
|
1531
|
+
border-block-end: 1px solid var(--sf-colour-border);
|
|
1532
|
+
padding-block: var(--sf-data-table-padding-block, var(--sf-space-xs));
|
|
1533
|
+
padding-inline: var(--sf-data-table-padding-inline, var(--sf-space-s));
|
|
1534
|
+
text-align: start;
|
|
1535
|
+
vertical-align: middle;
|
|
1536
|
+
}
|
|
1537
|
+
|
|
1538
|
+
.sf-data-table :where(th) {
|
|
1539
|
+
background: var(--sf-colour-surface-alt);
|
|
1540
|
+
color: var(--sf-colour-muted);
|
|
1541
|
+
font-size: var(--sf-step--1);
|
|
1542
|
+
font-weight: 800;
|
|
1543
|
+
}
|
|
1544
|
+
|
|
1545
|
+
.sf-data-table :where(tbody tr:hover) {
|
|
1546
|
+
background: color-mix(in oklch, var(--sf-colour-primary) 6%, transparent);
|
|
1547
|
+
}
|
|
1548
|
+
|
|
1549
|
+
.sf-data-table[data-density="compact"] {
|
|
1550
|
+
--sf-data-table-padding-block: var(--sf-space-2xs);
|
|
1551
|
+
--sf-data-table-padding-inline: var(--sf-space-xs);
|
|
1552
|
+
}
|
|
1553
|
+
|
|
1554
|
+
.sf-status {
|
|
1555
|
+
align-items: center;
|
|
1556
|
+
background: var(--sf-status-bg, var(--sf-colour-surface-alt));
|
|
1557
|
+
border: 1px solid var(--sf-status-border, var(--sf-colour-border));
|
|
1558
|
+
border-radius: var(--sf-radius-full);
|
|
1559
|
+
color: var(--sf-status-colour, var(--sf-colour-foreground));
|
|
1560
|
+
display: inline-flex;
|
|
1561
|
+
font-size: var(--sf-step--1);
|
|
1562
|
+
font-weight: 700;
|
|
1563
|
+
gap: var(--sf-space-3xs);
|
|
1564
|
+
line-height: 1;
|
|
1565
|
+
min-block-size: 1.75rem;
|
|
1566
|
+
padding-inline: .7em;
|
|
1567
|
+
}
|
|
1568
|
+
|
|
1569
|
+
.sf-status::before {
|
|
1570
|
+
background: currentColor;
|
|
1571
|
+
block-size: .42em;
|
|
1572
|
+
border-radius: var(--sf-radius-full);
|
|
1573
|
+
content: "";
|
|
1574
|
+
inline-size: .42em;
|
|
1575
|
+
}
|
|
1576
|
+
|
|
1577
|
+
.sf-status[data-tone="success"] {
|
|
1578
|
+
--sf-status-bg: var(--sf-colour-success-soft);
|
|
1579
|
+
--sf-status-border: color-mix(in oklch, var(--sf-colour-success) 34%, transparent);
|
|
1580
|
+
--sf-status-colour: color-mix(in oklch, var(--sf-colour-success) 76%, var(--sf-colour-foreground));
|
|
1581
|
+
}
|
|
1582
|
+
|
|
1583
|
+
.sf-status[data-tone="warning"] {
|
|
1584
|
+
--sf-status-bg: var(--sf-colour-warning-soft);
|
|
1585
|
+
--sf-status-border: color-mix(in oklch, var(--sf-colour-warning) 38%, transparent);
|
|
1586
|
+
--sf-status-colour: color-mix(in oklch, var(--sf-colour-warning) 70%, var(--sf-colour-foreground));
|
|
1587
|
+
}
|
|
1588
|
+
|
|
1589
|
+
.sf-status[data-tone="danger"] {
|
|
1590
|
+
--sf-status-bg: var(--sf-colour-danger-soft);
|
|
1591
|
+
--sf-status-border: color-mix(in oklch, var(--sf-colour-danger) 34%, transparent);
|
|
1592
|
+
--sf-status-colour: color-mix(in oklch, var(--sf-colour-danger) 76%, var(--sf-colour-foreground));
|
|
1593
|
+
}
|
|
1594
|
+
|
|
1595
|
+
.sf-status[data-tone="info"] {
|
|
1596
|
+
--sf-status-bg: var(--sf-colour-info-soft);
|
|
1597
|
+
--sf-status-border: color-mix(in oklch, var(--sf-colour-info) 34%, transparent);
|
|
1598
|
+
--sf-status-colour: color-mix(in oklch, var(--sf-colour-info) 76%, var(--sf-colour-foreground));
|
|
1599
|
+
}
|
|
1600
|
+
|
|
1601
|
+
.sf-data-list {
|
|
1602
|
+
display: grid;
|
|
1603
|
+
}
|
|
1604
|
+
|
|
1605
|
+
.sf-data-list > * {
|
|
1606
|
+
align-items: center;
|
|
1607
|
+
border-block-end: 1px solid var(--sf-colour-border);
|
|
1608
|
+
display: flex;
|
|
1609
|
+
flex-wrap: wrap;
|
|
1610
|
+
gap: var(--sf-space-xs);
|
|
1611
|
+
justify-content: space-between;
|
|
1612
|
+
padding-block: var(--sf-space-xs);
|
|
1613
|
+
}
|
|
1614
|
+
|
|
1615
|
+
.sf-data-list > :last-child {
|
|
1616
|
+
border-block-end: 0;
|
|
1617
|
+
}
|
|
1618
|
+
|
|
1619
|
+
.sf-empty-state {
|
|
1620
|
+
align-items: center;
|
|
1621
|
+
background: var(--sf-colour-surface-alt);
|
|
1622
|
+
border: 1px dashed var(--sf-colour-border-strong);
|
|
1623
|
+
border-radius: var(--sf-radius-panel);
|
|
1624
|
+
color: var(--sf-colour-muted);
|
|
1625
|
+
display: grid;
|
|
1626
|
+
gap: var(--sf-space-xs);
|
|
1627
|
+
justify-items: center;
|
|
1628
|
+
padding: var(--sf-space-l);
|
|
1629
|
+
text-align: center;
|
|
1630
|
+
}
|
|
1631
|
+
|
|
1632
|
+
.sf-settings-section,
|
|
1633
|
+
.sf-detail-panel {
|
|
1634
|
+
background: var(--sf-colour-surface);
|
|
1635
|
+
border: 1px solid var(--sf-colour-border);
|
|
1636
|
+
border-radius: var(--sf-radius-panel);
|
|
1637
|
+
display: grid;
|
|
1638
|
+
gap: var(--sf-space-s);
|
|
1639
|
+
padding: var(--sf-space-m);
|
|
1640
|
+
}
|
|
1641
|
+
|
|
1642
|
+
.sf-detail-panel {
|
|
1643
|
+
background: var(--sf-colour-surface-alt);
|
|
1644
|
+
}
|
|
1645
|
+
|
|
1471
1646
|
.sf-nav {
|
|
1472
1647
|
align-items: center;
|
|
1473
1648
|
display: flex;
|