@syncedco/flow 0.1.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.
Files changed (90) hide show
  1. package/CODE_OF_CONDUCT.md +26 -0
  2. package/CONTRIBUTING.md +54 -0
  3. package/LICENSE +21 -0
  4. package/README.md +334 -0
  5. package/SECURITY.md +30 -0
  6. package/SUPPORT.md +32 -0
  7. package/TRADEMARKS.md +14 -0
  8. package/base.css +100 -0
  9. package/bin/synced-flow.mjs +4639 -0
  10. package/components.css +1392 -0
  11. package/defaults.css +26 -0
  12. package/dist/config.d.ts +94 -0
  13. package/dist/config.js +3 -0
  14. package/dist/index.d.ts +45 -0
  15. package/dist/index.js +67 -0
  16. package/docs/accessibility-css.md +133 -0
  17. package/docs/ai-usage.md +112 -0
  18. package/docs/api-contract.md +81 -0
  19. package/docs/base-styling.md +113 -0
  20. package/docs/build-a-site-walkthrough.md +122 -0
  21. package/docs/cli-reference.md +230 -0
  22. package/docs/config-reference.md +81 -0
  23. package/docs/css-optimisation.md +117 -0
  24. package/docs/migration-from-tailwind.md +60 -0
  25. package/docs/native-components.md +156 -0
  26. package/docs/patterns.md +32 -0
  27. package/docs/presets.md +60 -0
  28. package/docs/quick-start.md +252 -0
  29. package/docs/recipes.md +285 -0
  30. package/docs/release-readiness.md +63 -0
  31. package/docs/system-primitives.md +150 -0
  32. package/docs/tailwind-comparison.md +66 -0
  33. package/docs/tokens.md +79 -0
  34. package/docs/website-patterns.md +114 -0
  35. package/docs/why-synced-flow.md +99 -0
  36. package/docs/wordpress.md +66 -0
  37. package/examples/README.md +16 -0
  38. package/examples/astro/package.json +19 -0
  39. package/examples/astro/src/pages/index.astro +85 -0
  40. package/examples/astro/src/styles/synced-flow.css +2 -0
  41. package/examples/astro/src/styles/synced-flow.generated.css +206 -0
  42. package/examples/astro/synced-flow.config.mjs +9 -0
  43. package/examples/next/app/layout.tsx +14 -0
  44. package/examples/next/app/page.tsx +92 -0
  45. package/examples/next/app/synced-flow.css +2 -0
  46. package/examples/next/app/synced-flow.generated.css +205 -0
  47. package/examples/next/package.json +19 -0
  48. package/examples/next/synced-flow.config.mjs +9 -0
  49. package/examples/plain-html/index.html +384 -0
  50. package/examples/plain-html/package.json +15 -0
  51. package/examples/plain-html/synced-flow.config.mjs +9 -0
  52. package/examples/plain-html/synced-flow.css +2 -0
  53. package/examples/plain-html/synced-flow.generated.css +205 -0
  54. package/examples/templates/README.md +22 -0
  55. package/examples/templates/blog-index.html +41 -0
  56. package/examples/templates/coming-soon.html +27 -0
  57. package/examples/templates/portfolio-scroll.html +45 -0
  58. package/examples/templates/saas-dashboard.html +171 -0
  59. package/examples/templates/saas-landing.html +104 -0
  60. package/examples/vite/index.html +2 -0
  61. package/examples/vite/package.json +20 -0
  62. package/examples/vite/src/main.jsx +27 -0
  63. package/examples/vite/src/synced-flow.css +2 -0
  64. package/examples/vite/src/synced-flow.generated.css +205 -0
  65. package/examples/vite/synced-flow.config.mjs +9 -0
  66. package/examples/wordpress/assets/css/synced-flow.css +641 -0
  67. package/examples/wordpress/functions.php +13 -0
  68. package/examples/wordpress/package.json +15 -0
  69. package/examples/wordpress/parts/footer.html +12 -0
  70. package/examples/wordpress/parts/header.html +10 -0
  71. package/examples/wordpress/patterns/contact-cta.php +28 -0
  72. package/examples/wordpress/patterns/feature-grid.php +38 -0
  73. package/examples/wordpress/patterns/landing-hero.php +45 -0
  74. package/examples/wordpress/synced-flow.config.mjs +11 -0
  75. package/examples/wordpress/templates/front-page.html +11 -0
  76. package/examples/wordpress/templates/index.html +27 -0
  77. package/examples/wordpress/theme.json +19 -0
  78. package/layout.css +365 -0
  79. package/package.json +93 -0
  80. package/reset.css +13 -0
  81. package/skills/synced-flow/SKILL.md +151 -0
  82. package/src/config.ts +98 -0
  83. package/src/index.ts +75 -0
  84. package/src/presets.d.mts +21 -0
  85. package/src/presets.mjs +171 -0
  86. package/src/tokens.mjs +138 -0
  87. package/src/utility-tokens.mjs +47 -0
  88. package/styles.css +2313 -0
  89. package/tokens.css +198 -0
  90. package/utilities.css +255 -0
package/defaults.css ADDED
@@ -0,0 +1,26 @@
1
+ /* Generated by @syncedco/flow. Edit src/tokens.mjs or scripts/build-css.mjs, then run pnpm build. */
2
+ @layer reset, tokens, base, app, layout, components, utilities;
3
+ @layer app {
4
+ :where(a) {
5
+ color: inherit;
6
+ text-decoration: none;
7
+ }
8
+
9
+ :where(ol, ul, menu) {
10
+ list-style: none;
11
+ padding-inline-start: 0;
12
+ }
13
+
14
+ :where(button) {
15
+ background: transparent;
16
+ border: 0;
17
+ color: inherit;
18
+ }
19
+
20
+ :where(fieldset) {
21
+ border: 0;
22
+ padding: 0;
23
+ }
24
+
25
+ :where(legend) { padding: 0; }
26
+ }
@@ -0,0 +1,94 @@
1
+ type TokenMap = Record<string, string>;
2
+ export type SyncedFlowTheme = {
3
+ /**
4
+ * Project font stacks. Values should be complete CSS font-family values.
5
+ */
6
+ fonts?: Partial<Record<'sans' | 'display' | 'mono', string>>;
7
+ /**
8
+ * Semantic colour tokens. Use OKLCH where possible.
9
+ */
10
+ colours?: TokenMap;
11
+ /**
12
+ * Semantic colour overrides for .sf-theme-dark or [data-sf-theme="dark"].
13
+ */
14
+ darkColours?: TokenMap;
15
+ /**
16
+ * Radius scale overrides such as md, lg, xl, full.
17
+ */
18
+ radii?: TokenMap;
19
+ /**
20
+ * Layout-level tokens.
21
+ */
22
+ layout?: {
23
+ containerMax?: string;
24
+ gutter?: string;
25
+ columns?: number;
26
+ };
27
+ /**
28
+ * Component-level token overrides.
29
+ */
30
+ components?: {
31
+ button?: {
32
+ radius?: string;
33
+ blockSize?: string;
34
+ paddingInline?: string;
35
+ };
36
+ card?: {
37
+ radius?: string;
38
+ padding?: string;
39
+ shadow?: string;
40
+ };
41
+ input?: {
42
+ radius?: string;
43
+ blockSize?: string;
44
+ };
45
+ };
46
+ };
47
+ export type SyncedFlowConfig = {
48
+ /**
49
+ * Directory used to resolve scan and output paths. Defaults to the current
50
+ * working directory.
51
+ */
52
+ cwd?: string;
53
+ /**
54
+ * Source directories that the CLI scans for class tokens.
55
+ */
56
+ scan?: string[];
57
+ /**
58
+ * Class tokens to always generate when they are composed dynamically.
59
+ */
60
+ safelist?: string[];
61
+ /**
62
+ * Generated CSS output file.
63
+ */
64
+ out?: string;
65
+ /**
66
+ * Project token overrides emitted into the generated CSS.
67
+ */
68
+ theme?: SyncedFlowTheme;
69
+ /**
70
+ * Include reset, base, layout, and component CSS in the generated file.
71
+ * Most projects should import @syncedco/flow/styles.css and leave this false.
72
+ */
73
+ includeCore?: boolean;
74
+ /**
75
+ * Include site/UI defaults when includeCore is true. For modular projects,
76
+ * import @syncedco/flow/defaults.css from the CSS entry instead.
77
+ */
78
+ includeDefaults?: boolean;
79
+ /**
80
+ * Enable breakpoint-style variants such as sm:, md:, lg:, and xl:.
81
+ * Leave false for strict fluid projects; enable only during migrations.
82
+ */
83
+ responsiveVariants?: boolean;
84
+ /**
85
+ * Fail the CLI when unsupported class tokens are detected.
86
+ */
87
+ failOnUnsupported?: boolean;
88
+ /**
89
+ * Suppress non-critical CLI warnings.
90
+ */
91
+ quiet?: boolean;
92
+ };
93
+ export declare function defineConfig(config: SyncedFlowConfig): SyncedFlowConfig;
94
+ export {};
package/dist/config.js ADDED
@@ -0,0 +1,3 @@
1
+ export function defineConfig(config) {
2
+ return config;
3
+ }
@@ -0,0 +1,45 @@
1
+ export { defineConfig, type SyncedFlowConfig, type SyncedFlowTheme } from './config.js';
2
+ export { presetNames, themePresets } from '../src/presets.mjs';
3
+ export type ClassValue = string | number | boolean | null | undefined | ClassValue[] | Record<string, unknown>;
4
+ export declare function cx(...inputs: ClassValue[]): string;
5
+ export declare const fluidSystem: {
6
+ readonly layout: {
7
+ readonly container: "sf-container";
8
+ readonly section: "sf-section";
9
+ readonly stack: "sf-stack";
10
+ readonly cluster: "sf-cluster";
11
+ readonly repel: "sf-repel";
12
+ readonly grid: "sf-grid";
13
+ readonly autoGrid: "sf-auto-grid";
14
+ readonly sidebar: "sf-sidebar";
15
+ readonly switcher: "sf-switcher";
16
+ readonly frame: "sf-frame";
17
+ readonly cover: "sf-cover";
18
+ readonly flow: "sf-flow";
19
+ };
20
+ readonly components: {
21
+ readonly button: "sf-button";
22
+ readonly card: "sf-card";
23
+ readonly badge: "sf-badge";
24
+ readonly field: "sf-field";
25
+ readonly input: "sf-input";
26
+ };
27
+ readonly utilities: {
28
+ readonly visuallyHidden: "sf-visually-hidden";
29
+ readonly notVisuallyHidden: "sf-not-visually-hidden";
30
+ readonly srOnly: "sr-only";
31
+ readonly notSrOnly: "not-sr-only";
32
+ readonly skipLink: "sf-skip-link";
33
+ readonly focusRing: "sf-focus-ring";
34
+ readonly focusRingInset: "sf-focus-ring-inset";
35
+ readonly touchTarget: "sf-touch-target";
36
+ readonly listReset: "sf-list-reset";
37
+ readonly listDisc: "sf-list-disc";
38
+ readonly listDecimal: "sf-list-decimal";
39
+ readonly link: "sf-link";
40
+ readonly linkSubtle: "sf-link-subtle";
41
+ readonly linkPlain: "sf-link-plain";
42
+ readonly prose: "sf-prose";
43
+ };
44
+ };
45
+ export type FluidSystem = typeof fluidSystem;
package/dist/index.js ADDED
@@ -0,0 +1,67 @@
1
+ export { defineConfig } from './config.js';
2
+ export { presetNames, themePresets } from '../src/presets.mjs';
3
+ export function cx(...inputs) {
4
+ const classes = [];
5
+ for (const input of inputs)
6
+ appendClassValue(classes, input);
7
+ return classes.join(' ');
8
+ }
9
+ function appendClassValue(classes, value) {
10
+ if (!value)
11
+ return;
12
+ if (typeof value === 'string' || typeof value === 'number') {
13
+ classes.push(String(value));
14
+ return;
15
+ }
16
+ if (Array.isArray(value)) {
17
+ for (const item of value)
18
+ appendClassValue(classes, item);
19
+ return;
20
+ }
21
+ if (typeof value === 'object') {
22
+ for (const [className, enabled] of Object.entries(value)) {
23
+ if (enabled)
24
+ classes.push(className);
25
+ }
26
+ }
27
+ }
28
+ export const fluidSystem = {
29
+ layout: {
30
+ container: 'sf-container',
31
+ section: 'sf-section',
32
+ stack: 'sf-stack',
33
+ cluster: 'sf-cluster',
34
+ repel: 'sf-repel',
35
+ grid: 'sf-grid',
36
+ autoGrid: 'sf-auto-grid',
37
+ sidebar: 'sf-sidebar',
38
+ switcher: 'sf-switcher',
39
+ frame: 'sf-frame',
40
+ cover: 'sf-cover',
41
+ flow: 'sf-flow',
42
+ },
43
+ components: {
44
+ button: 'sf-button',
45
+ card: 'sf-card',
46
+ badge: 'sf-badge',
47
+ field: 'sf-field',
48
+ input: 'sf-input',
49
+ },
50
+ utilities: {
51
+ visuallyHidden: 'sf-visually-hidden',
52
+ notVisuallyHidden: 'sf-not-visually-hidden',
53
+ srOnly: 'sr-only',
54
+ notSrOnly: 'not-sr-only',
55
+ skipLink: 'sf-skip-link',
56
+ focusRing: 'sf-focus-ring',
57
+ focusRingInset: 'sf-focus-ring-inset',
58
+ touchTarget: 'sf-touch-target',
59
+ listReset: 'sf-list-reset',
60
+ listDisc: 'sf-list-disc',
61
+ listDecimal: 'sf-list-decimal',
62
+ link: 'sf-link',
63
+ linkSubtle: 'sf-link-subtle',
64
+ linkPlain: 'sf-link-plain',
65
+ prose: 'sf-prose',
66
+ },
67
+ };
@@ -0,0 +1,133 @@
1
+ # Accessibility CSS
2
+
3
+ Synced Flow does not run accessibility audits. It provides CSS affordances so
4
+ semantic HTML and ARIA states are visible, consistent, and easy to compose.
5
+
6
+ ## Principles
7
+
8
+ - Prefer native elements first: `button`, `a[href]`, `label`, `input`,
9
+ `select`, `textarea`, `details`, `summary`, `nav`, `main`, `section`, and
10
+ `footer`.
11
+ - Use ARIA only when native semantics do not express the state.
12
+ - Keep accessible names and descriptions in markup; Synced Flow styles the
13
+ visible states.
14
+ - Use project-level accessibility testing in the consuming app.
15
+
16
+ ## Built-In Affordances
17
+
18
+ | Hook | Styled behavior |
19
+ | --- | --- |
20
+ | `:focus-visible` | Visible outline using `--sf-colour-ring`, with forced-colors support. |
21
+ | `:target` | Scroll margin for skip links and anchor navigation. |
22
+ | `.sf-skip-link` | Keyboard-visible skip link. |
23
+ | `.sf-visually-hidden`, `.sr-only` | Visually hidden text that remains available to assistive tech. |
24
+ | `.sf-touch-target` | Minimum interactive target sizing. |
25
+ | `[aria-current="page"]`, `[aria-current="true"]` | Current nav/link state. |
26
+ | `[aria-expanded="true"]`, `[aria-pressed="true"]`, `[aria-selected="true"]`, `[data-state="open"]` | Active disclosure/toggle/selection state for buttons and nav links. |
27
+ | `[aria-disabled="true"]`, `:disabled` | Disabled affordance for buttons, links, cards, and form controls. |
28
+ | `[aria-busy="true"]`, `[data-loading="true"]` | Busy/loading affordance for buttons and form controls. |
29
+ | `.sf-field[data-invalid="true"]`, `[aria-invalid="true"]` | Invalid field styling. |
30
+ | `.sf-required`, `.sf-label[aria-required="true"]`, `label:has(+ :required)` | Required-field marker styling. |
31
+ | `@media (forced-colors: active)` | High contrast mode border, focus, and button fallbacks. |
32
+
33
+ ## Forms
34
+
35
+ Pair visible help and error text with `aria-describedby`. Use
36
+ `aria-invalid="true"` only when a field is currently invalid.
37
+
38
+ ```html
39
+ <div class="sf-field" data-invalid="true">
40
+ <label class="sf-required" for="email">Email</label>
41
+ <input
42
+ class="sf-input"
43
+ id="email"
44
+ name="email"
45
+ type="email"
46
+ required
47
+ aria-invalid="true"
48
+ aria-describedby="email-help email-error"
49
+ />
50
+ <p class="sf-help" id="email-help">Use a work email address.</p>
51
+ <p class="sf-error" id="email-error">Enter a valid email address.</p>
52
+ </div>
53
+ ```
54
+
55
+ Use `disabled` for native disabled controls. Use `aria-disabled="true"` only
56
+ when an element cannot use the native `disabled` attribute, such as an anchor
57
+ that is visually present but intentionally unavailable.
58
+
59
+ ## Navigation And Disclosure
60
+
61
+ Use `aria-current="page"` for the current page link.
62
+
63
+ ```html
64
+ <nav class="sf-nav" aria-label="Primary">
65
+ <ul class="sf-nav__list">
66
+ <li><a class="sf-nav__link" href="/" aria-current="page">Home</a></li>
67
+ <li><a class="sf-nav__link" href="/pricing">Pricing</a></li>
68
+ </ul>
69
+ </nav>
70
+ ```
71
+
72
+ Use native `details`/`summary` for FAQ and simple disclosure content.
73
+
74
+ ```html
75
+ <details class="sf-faq__item">
76
+ <summary>Can I customize the theme?</summary>
77
+ <p>Yes. Override semantic tokens before adding custom CSS.</p>
78
+ </details>
79
+ ```
80
+
81
+ For JavaScript-powered menus or toggles, use real buttons and update
82
+ `aria-expanded`, `aria-pressed`, or `aria-selected` as appropriate. Synced Flow
83
+ styles those states for `.sf-button` and `.sf-nav__link`.
84
+
85
+ For native dialog, popover, drawer, tooltip, tabs, and disclosure markup, see
86
+ [Native Components](native-components.md). Synced Flow styles those browser
87
+ primitives but does not ship JavaScript components.
88
+
89
+ ## Alerts And Status
90
+
91
+ Synced Flow alert variants are visual styles. Choose the live-region behavior
92
+ in markup:
93
+
94
+ ```html
95
+ <div class="sf-alert sf-alert--success" role="status">
96
+ <p class="sf-alert__title">Saved</p>
97
+ <p>Your changes were saved.</p>
98
+ </div>
99
+
100
+ <div class="sf-alert sf-alert--danger" role="alert">
101
+ <p class="sf-alert__title">Payment failed</p>
102
+ <p>Check the card details and try again.</p>
103
+ </div>
104
+ ```
105
+
106
+ Use `role="status"` for polite updates. Use `role="alert"` only for urgent
107
+ messages that need immediate announcement.
108
+
109
+ ## Busy And Loading States
110
+
111
+ When an action is in progress, keep the accessible name visible and add state:
112
+
113
+ ```html
114
+ <button class="sf-button" type="button" aria-busy="true">
115
+ Saving
116
+ </button>
117
+ ```
118
+
119
+ Synced Flow styles this as a busy state, but the consuming app should still
120
+ manage focus, state changes, and completion messaging.
121
+
122
+ ## Confidence Checklist
123
+
124
+ Use this when changing the CSS system or examples:
125
+
126
+ - Navigate the demo with keyboard only.
127
+ - Confirm focus is visible on links, buttons, form controls, summaries, and the
128
+ skip link.
129
+ - Confirm invalid, required, disabled, busy, current, expanded, selected, and
130
+ pressed states remain visible when those states are present in markup.
131
+ - Confirm `details`/`summary` works without JavaScript.
132
+ - Confirm forced-colors styles do not depend only on subtle background colour.
133
+ - Confirm examples use native elements before ARIA.
@@ -0,0 +1,112 @@
1
+ # AI Agent Setup And Usage
2
+
3
+ Use this when an AI agent is building or editing a project with Synced Flow.
4
+
5
+ Synced Flow ships a skill at
6
+ [`skills/synced-flow/SKILL.md`](../skills/synced-flow/SKILL.md). In a
7
+ consumer project, make it discoverable with project-level guidance:
8
+
9
+ ```bash
10
+ pnpm exec synced-flow agents install
11
+ pnpm exec synced-flow agents status
12
+ pnpm exec synced-flow skill
13
+ ```
14
+
15
+ Use `agents install --target all` to add project-local guidance for Cursor,
16
+ Codex-style agents, Claude, Copilot, Windsurf, Gemini, and Aider where those
17
+ tools have clear project conventions.
18
+
19
+ ## First Moves
20
+
21
+ 1. Install the package.
22
+ 2. Run `synced-flow init --preset <framework> --agents`.
23
+ 3. Ask for a short theme brief: radius, fonts, primary colour, accent colour,
24
+ surface style, and density.
25
+ 4. Convert that brief into `synced-flow.config.mjs` theme tokens.
26
+ 5. Import the generated CSS entry once.
27
+ 6. Run `synced-flow catalog --json` before choosing recipes and classes.
28
+ 7. Run `synced-flow pattern --list` before hand-rolling interaction markup.
29
+ 8. Run `synced-flow lint --json` and `synced-flow doctor` before finishing.
30
+
31
+ Good theme prompt:
32
+
33
+ ```text
34
+ Use the Synced Flow skill. Build a theme config for a modern B2B website:
35
+ soft but not pill-shaped radius, system sans UI, editorial display headings,
36
+ blue primary, green accent, light raised cards, and spacious sections.
37
+ Return only the Synced Flow config theme object.
38
+ ```
39
+
40
+ For a file-based workflow, put the answers in `brief.md` and run:
41
+
42
+ ```bash
43
+ pnpm exec synced-flow theme init --from brief.md
44
+ pnpm exec synced-flow theme init --from brief.md --preset-base neutral-saas
45
+ ```
46
+
47
+ If the output includes warnings, ask the user for the missing brand decisions
48
+ before finalising the theme.
49
+
50
+ ## Styling Rules
51
+
52
+ - Prefer `sf-container`, `sf-section`, `sf-stack`, `sf-cluster`, `sf-auto-grid`,
53
+ `sf-split`, and `sf-sidebar` before writing custom layout CSS.
54
+ - Use semantic colours: `bg-background`, `text-foreground`, `bg-primary`,
55
+ `text-primary-foreground`, `border-border`, `bg-surface`.
56
+ - Use `sf-button`, `sf-card`, `sf-badge`, `sf-field`, and `sf-input` for common UI.
57
+ - Keep browser affordances unless the UI intentionally replaces them: body
58
+ links stay underlined, content lists keep markers, and focus states remain
59
+ visible.
60
+ - Use `@syncedco/flow/defaults.css` for common site/UI defaults when raw links should
61
+ not be underlined and menu lists should not show bullets. Add it with
62
+ `synced-flow add defaults` if a project was initialised without it.
63
+ - Use `sr-only` / `not-sr-only`, `sf-skip-link`, `sf-focus-ring`,
64
+ `sf-touch-target`, `sf-list-reset`, `sf-link`, and `sf-link-plain` for
65
+ accessibility and UI affordance work.
66
+ - Use theme presets or config `theme` overrides for brand choices.
67
+ - Put repeated brand decisions in theme tokens before adding custom CSS.
68
+ - Use `synced-flow suggest "<brief>"` to choose section recipes before adding
69
+ new one-off patterns.
70
+ - Use `synced-flow suggest "<brief>" --scaffold --framework <target> --dry-run`
71
+ when starting a page or project from a brief.
72
+ - Use `synced-flow pattern <id> --markup` for complete native interaction
73
+ patterns such as mobile drawers, scroll sections, popover drawers, and native
74
+ dialogs.
75
+ - Use `synced-flow recipe <id> --markup` to get copy-ready page sections for
76
+ SaaS, portfolio, agency, blog, article, about, team, contact, 404, and coming
77
+ soon pages.
78
+ - Choose `saas-landing` for public SaaS marketing pages. Choose
79
+ `saas-dashboard` for authenticated app UI, admin panels, portals, CRMs,
80
+ analytics dashboards, metrics, tables, account menus, and login state.
81
+ - Treat auth recipe markup as UI only; sessions, providers, permissions, and
82
+ sign-out logic belong to the consuming app.
83
+ - Keep class names complete in source files. Do not build classes from fragments.
84
+ - Use `safelist` only when dynamic classes are unavoidable.
85
+ - Do not enable `responsiveVariants` in new projects.
86
+
87
+ ## Good Starter Shape
88
+
89
+ ```html
90
+ <main class="sf-section">
91
+ <section class="sf-container sf-stack">
92
+ <p class="sf-kicker">Practical systems</p>
93
+ <h1 class="sf-text-display">Fluid from the first screen.</h1>
94
+ <p class="sf-text-lead sf-prose">Use tokens and primitives before one-off CSS.</p>
95
+ <div class="sf-cluster">
96
+ <a class="sf-button sf-button--default" href="/contact">Start discovery</a>
97
+ <a class="sf-button sf-button--outline" href="/docs">Read docs</a>
98
+ </div>
99
+ </section>
100
+ </main>
101
+ ```
102
+
103
+ ## Finish Checklist
104
+
105
+ ```bash
106
+ pnpm flow:build
107
+ pnpm flow:check
108
+ pnpm exec synced-flow lint --json
109
+ pnpm flow:doctor
110
+ ```
111
+
112
+ If `doctor` warns about stale CSS, run `pnpm flow:build`.
@@ -0,0 +1,81 @@
1
+ # CSS API Contract
2
+
3
+ Synced Flow is small enough to read, but projects still need to know which
4
+ parts are safe to rely on. Treat this page as the public CSS contract for the
5
+ 0.x line.
6
+
7
+ ## Stable Public Surface
8
+
9
+ These are intended for application code and examples.
10
+
11
+ | Surface | Public API |
12
+ | --- | --- |
13
+ | Imports | `@syncedco/flow/styles.css`, `tokens.css`, `reset.css`, `base.css`, `defaults.css`, `layout.css`, `components.css`, `utilities.css` |
14
+ | Tokens | `--sf-*` custom properties emitted by `tokens.css` |
15
+ | Theme config | `theme.fonts`, `theme.colours`, `theme.darkColours`, `theme.radii`, `theme.layout`, `theme.components` |
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` |
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
+ | Website patterns | `sf-logo-cloud`, `sf-feature`, `sf-stats`, `sf-testimonial`, `sf-pricing-grid`, `sf-price-card`, `sf-faq`, `sf-cta`, `sf-footer` |
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` |
21
+ | CLI | `synced-flow init`, `agents install`, `agents status`, `skill`, `add defaults`, `build`, `watch`, `lint`, `doctor`, `tokens`, `catalog`, `suggest`, `pattern`, `recipe`, `theme init`, `theme validate` |
22
+
23
+ ## AI Agent Contract
24
+
25
+ The project-level AI setup commands are public in 0.x:
26
+
27
+ - `synced-flow agents install [--target universal|cursor|codex|claude|copilot|windsurf|gemini|aider|all] [--force] [--dry-run]`
28
+ - `synced-flow agents status`
29
+ - `synced-flow skill`
30
+ - `synced-flow pattern <id> [--framework html|next|react|astro] [--markup|--json]`
31
+ - `synced-flow pattern --list`
32
+ - `synced-flow suggest "<brief>" --scaffold [--framework next|vite|astro|plain] [--out dir] [--dry-run] [--force]`
33
+ - `synced-flow lint [--json] [--fix] [paths...]`
34
+
35
+ `catalog --json` includes `patterns[]` with copy-ready interaction metadata:
36
+ classes, markup, JS requirement notes, accessibility notes, and gotchas.
37
+
38
+ ## Internal Or Compatibility Surface
39
+
40
+ These can change more freely.
41
+
42
+ - Generated compatibility utility selectors such as `[class~="text-primary"]`.
43
+ - Tailwind-migration helpers enabled by `responsiveVariants`.
44
+ - Implementation details inside `scripts/build-css.mjs`.
45
+ - Utility-compatible aliases such as `--color-*`, `--font-*`, and `--radius-*`.
46
+ They are useful for migration output, but `--sf-*` tokens are the preferred
47
+ long-term API.
48
+
49
+ ## Unit Policy
50
+
51
+ Design decisions should use `rem`, fluid `clamp()` tokens, logical properties,
52
+ or percentages. Raw `px` is reserved for:
53
+
54
+ - `1px` borders and inset hairlines.
55
+ - Forced-colors/system fallback outlines.
56
+ - Internal generator math that is emitted as `rem` or `clamp()`.
57
+
58
+ `pnpm guardrails` enforces this policy against the shipped CSS files.
59
+
60
+ ## Change Rules
61
+
62
+ - Additive tokens/classes are safe in minor releases.
63
+ - Renaming or removing public `--sf-*` tokens or `sf-*` classes needs a
64
+ migration note and should wait for a major release once the project leaves
65
+ `0.x`.
66
+ - New primitives should earn their place by replacing repeated website/app
67
+ CSS, not by chasing every utility class from larger frameworks.
68
+ - Prefer documentation recipes before adding new CSS.
69
+
70
+ ## Stability Checklist
71
+
72
+ Before changing the public surface, run:
73
+
74
+ ```bash
75
+ pnpm build
76
+ pnpm check
77
+ pnpm test
78
+ ```
79
+
80
+ `pnpm check` includes generated CSS freshness, type checks, CSS size budgets,
81
+ dependency checks, layer-shape checks, and raw-pixel guardrails.
@@ -0,0 +1,113 @@
1
+ # Base Styling Decisions
2
+
3
+ Synced Flow uses a conservative reset and modern base layer. The goal is to
4
+ make new projects consistent without hiding important browser affordances.
5
+
6
+ ## What Stays Native
7
+
8
+ - Links stay visibly underlined by default.
9
+ - `ul` and `ol` keep their markers by default.
10
+ - Form controls inherit project typography but keep their native semantics.
11
+ - Focus styles are visible through `:focus-visible`.
12
+ - Headings, code, blockquotes, horizontal rules, and selection states get
13
+ token-based defaults without removing their native meaning.
14
+
15
+ ## Optional App Defaults
16
+
17
+ Most app and marketing-site interfaces do not want raw link underlines in
18
+ navigation, or bullets on menu lists. Add the optional defaults layer for those
19
+ project-wide UI defaults:
20
+
21
+ ```css
22
+ @import "@syncedco/flow/defaults.css";
23
+ ```
24
+
25
+ You can also add it later with the CLI:
26
+
27
+ ```bash
28
+ pnpm exec synced-flow add defaults --file src/synced-flow.css
29
+ ```
30
+
31
+ `defaults.css` removes raw link underlines, resets `ol`/`ul`/`menu` markers and start
32
+ padding, and strips basic button/fieldset chrome. Use `sf-link`, `sf-list-disc`,
33
+ `sf-list-decimal`, or `sf-prose` where content needs visible semantics again.
34
+
35
+ Use opt-in utilities when a component needs a different treatment:
36
+
37
+ ```html
38
+ <nav aria-label="Primary">
39
+ <ul class="sf-list-reset sf-cluster">
40
+ <li><a class="sf-link-plain" href="/">Home</a></li>
41
+ <li><a class="sf-link-plain" href="/docs">Docs</a></li>
42
+ </ul>
43
+ </nav>
44
+ ```
45
+
46
+ ## Accessibility Utilities
47
+
48
+ ```html
49
+ <a class="sf-skip-link" href="#main">Skip to main content</a>
50
+ <span class="sr-only">Opens in a new tab</span>
51
+ <button class="sf-touch-target sf-focus-ring">Save</button>
52
+ ```
53
+
54
+ Available helpers:
55
+
56
+ - `sr-only` / `sf-visually-hidden`
57
+ - `not-sr-only` / `sf-not-visually-hidden`
58
+ - `sf-skip-link`
59
+ - `sf-focus-ring`
60
+ - `sf-focus-ring-inset`
61
+ - `sf-touch-target`
62
+
63
+ ## Link Utilities
64
+
65
+ - `sf-link` for primary inline links.
66
+ - `sf-link-subtle` for inherited-colour inline links.
67
+ - `sf-link-plain` for navigation, buttons, cards, and other UI where the
68
+ element has another clear affordance.
69
+
70
+ Body and prose links should normally stay visibly identifiable.
71
+
72
+ ## Forms And UI Components
73
+
74
+ The base layer keeps native form semantics, while component classes provide
75
+ ready-to-use styling:
76
+
77
+ ```html
78
+ <form class="sf-form">
79
+ <div class="sf-field">
80
+ <label for="email">Email</label>
81
+ <input class="sf-input" id="email" type="email" />
82
+ <p class="sf-help">We only use this for project updates.</p>
83
+ </div>
84
+ <button class="sf-button" type="submit">Send</button>
85
+ </form>
86
+ ```
87
+
88
+ Use `sf-input`, `sf-select`, `sf-textarea`, `sf-check`, `sf-help`, and
89
+ `sf-error` for common form needs. Use `sf-alert` variants for notices and
90
+ feedback.
91
+
92
+ Synced Flow also styles accessible states such as `[aria-invalid="true"]`,
93
+ `[aria-disabled="true"]`, `[aria-busy="true"]`, `[aria-current="page"]`, and
94
+ required-field markers. See [Accessibility CSS](accessibility-css.md) for the
95
+ markup contract.
96
+
97
+ ## List Utilities
98
+
99
+ - `sf-list-reset` removes list markers and start padding for navigation or UI
100
+ lists.
101
+ - `sf-list-disc` restores disc markers.
102
+ - `sf-list-decimal` restores numbered markers.
103
+
104
+ Do not reset content lists just to remove browser defaults. Keep markers when
105
+ they carry meaning.
106
+
107
+ ## Modern CSS Baseline
108
+
109
+ The base layer uses cascade layers, logical properties, low-specificity
110
+ `:where()` selectors, OKLCH-aware `color-mix()`, `:focus-visible`, and
111
+ `prefers-reduced-motion`. Utopia informs the fluid type, space, and grid
112
+ tokens; Synced Flow owns the reset, accessibility helpers, and component
113
+ defaults.