@uniflowed/stylex 0.1.0 → 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/index.js CHANGED
@@ -21,7 +21,7 @@
21
21
  //
22
22
  // 1. **A token set that already exists.** `@uniflowed/stylex/tokens.stylex.js`
23
23
  // is a real `stylex.defineVars` module — colour roles, a type scale,
24
- // spacing, radii, elevation and motion — so a project has a coherent palette
24
+ // spacing, radii and motion — so a project has a coherent palette
25
25
  // without authoring one, and gets it as `:root` custom properties the build
26
26
  // inlined rather than as anything computed in a browser.
27
27
  // 2. **A base layer over those tokens.** `@uniflowed/stylex/preset` is
@@ -67,14 +67,17 @@
67
67
  //
68
68
  // # Where the token values come from
69
69
  //
70
- // `@uniflowed/brand` owns uf's visual identity — the palette, the type,
71
- // spacing and radius scales — and it stays there. It cannot own the token
72
- // module: a StyleX token's name is computed by the compiler from the binding
73
- // and key it was declared under, `defineVars` accepts only literals, and
74
- // brand's `--uf-*` names are hand-written for a different consumer. So brand
75
- // holds the identity values and `./tokens.stylex.js` is their StyleX-shaped
76
- // projection into semantic roles — `accent`, `ink`, `canvas` — which is the
77
- // layer a design system needs and an identity does not have.
70
+ // `@uniflowed/brand` owns uf's visual identity — the logo, its spectrum, the
71
+ // type and spacing scales — and it stays there. The interface borrows brand's
72
+ // type and spacing, and chooses its own colours and corners: a UI dressed in
73
+ // the logo's cyan-to-magenta spectrum and round corners looks generated, so
74
+ // `./tokens.stylex.js` holds a neutral-first palette of its own
75
+ // (`docs/ui-visual-language.md` says why). Brand could not own the token
76
+ // module anyway: a StyleX token's name is computed by the compiler from the
77
+ // binding and key it was declared under, and `defineVars` accepts only
78
+ // literals. So `./tokens.stylex.js` is where the semantic roles — `accent`,
79
+ // `ink`, `canvas` — get their values, which is the layer a design system needs
80
+ // and an identity does not have.
78
81
  //
79
82
  // # Readiness
80
83
  //
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@uniflowed/stylex",
3
- "version": "0.1.0",
3
+ "version": "0.2.0",
4
4
  "description": "Flow declarations for @uniflowed/stylex, part of the Unified Toolchain for Flow.",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -23,7 +23,7 @@
23
23
  "!*.test.js"
24
24
  ],
25
25
  "dependencies": {
26
- "@uniflowed/core": "0.1.0"
26
+ "@uniflowed/core": "0.2.0"
27
27
  },
28
28
  "peerDependencies": {
29
29
  "react-native": ">=0.86.0"
package/preset.js CHANGED
@@ -20,9 +20,9 @@
20
20
  //
21
21
  // It is the only module in the package that has opinions. `props` is a merge,
22
22
  // the tokens are names, the themes are values; this is where uf says a card has
23
- // a 16px radius. A project that wants its own look drops this import and keeps
24
- // everything else, and a bundler drops the rules with it because nothing else
25
- // references them.
23
+ // a 6px radius and a 1px border rather than a shadow. A project that wants its
24
+ // own look drops this import and keeps everything else, and a bundler drops
25
+ // the rules with it because nothing else references them.
26
26
  //
27
27
  // # Why functions rather than exported namespaces
28
28
  //
@@ -84,13 +84,14 @@ const surfaces = stylex.create({
84
84
  borderColor: ufTokens.border,
85
85
  borderRadius: ufTokens.radiusLg,
86
86
  padding: ufTokens.space6,
87
- boxShadow: ufTokens.shadowCard,
88
87
  },
89
88
  panel: {
90
89
  backgroundColor: ufTokens.surface,
91
- borderRadius: ufTokens.radiusXl,
90
+ borderWidth: "1px",
91
+ borderStyle: "solid",
92
+ borderColor: ufTokens.border,
93
+ borderRadius: ufTokens.radiusLg,
92
94
  padding: ufTokens.space6,
93
- boxShadow: ufTokens.shadowPanel,
94
95
  },
95
96
  sunken: {
96
97
  backgroundColor: ufTokens.sunken,
@@ -132,7 +133,10 @@ const buttons = stylex.create({
132
133
  borderColor: "transparent",
133
134
  cursor: "pointer",
134
135
  transitionProperty: "background-color, border-color, color",
135
- transitionDuration: ufTokens.durationFast,
136
+ transitionDuration: {
137
+ default: ufTokens.durationFast,
138
+ "@media (prefers-reduced-motion: reduce)": "0s",
139
+ },
136
140
  transitionTimingFunction: ufTokens.easing,
137
141
  // The ring is drawn only for a keyboard focus, which is the whole reason
138
142
  // `:focus-visible` exists: a mouse click should not light the control up.
@@ -191,7 +195,7 @@ const fields = stylex.create({
191
195
  borderWidth: "1px",
192
196
  borderStyle: "solid",
193
197
  borderColor: ufTokens.border,
194
- borderRadius: ufTokens.radiusSm,
198
+ borderRadius: ufTokens.radiusMd,
195
199
  paddingBlock: ufTokens.space2,
196
200
  paddingInline: ufTokens.space3,
197
201
  outlineWidth: { default: "0", ":focus-visible": "2px" },
@@ -226,9 +230,11 @@ const overlays = stylex.create({
226
230
  backgroundColor: ufTokens.surface,
227
231
  color: ufTokens.ink,
228
232
  fontFamily: ufTokens.fontSans,
229
- borderRadius: ufTokens.radiusXl,
233
+ borderWidth: "1px",
234
+ borderStyle: "solid",
235
+ borderColor: ufTokens.border,
236
+ borderRadius: ufTokens.radiusLg,
230
237
  padding: ufTokens.space6,
231
- boxShadow: ufTokens.shadowPanel,
232
238
  },
233
239
  });
234
240
 
@@ -243,7 +249,6 @@ const menus = stylex.create({
243
249
  borderStyle: "solid",
244
250
  borderColor: ufTokens.border,
245
251
  borderRadius: ufTokens.radiusMd,
246
- boxShadow: ufTokens.shadowPanel,
247
252
  },
248
253
  item: {
249
254
  display: "flex",
@@ -315,7 +320,10 @@ const controls = stylex.create({
315
320
  color: ufTokens.accentInk,
316
321
  cursor: "pointer",
317
322
  transitionProperty: "background-color, border-color",
318
- transitionDuration: ufTokens.durationFast,
323
+ transitionDuration: {
324
+ default: ufTokens.durationFast,
325
+ "@media (prefers-reduced-motion: reduce)": "0s",
326
+ },
319
327
  transitionTimingFunction: ufTokens.easing,
320
328
  outlineWidth: { default: "0", ":focus-visible": "2px" },
321
329
  outlineStyle: "solid",
@@ -325,7 +333,7 @@ const controls = stylex.create({
325
333
  box: {
326
334
  width: ufTokens.sizeControl,
327
335
  height: ufTokens.sizeControl,
328
- borderRadius: "5px",
336
+ borderRadius: ufTokens.radiusSm,
329
337
  },
330
338
  // A switch's track is the one piece of geometry here that is not a token:
331
339
  // nothing but a switch reads it, and a theme that changed it would leave the
package/theme.js CHANGED
@@ -62,25 +62,23 @@ import { ufTokens } from "./tokens.stylex.js";
62
62
  * exactly one `@media (prefers-color-scheme: dark)` block.
63
63
  */
64
64
  export const ufAutoTheme = stylex.createTheme(ufTokens, {
65
- canvas: { "@media (prefers-color-scheme: dark)": "#0b1220" },
66
- sunken: { "@media (prefers-color-scheme: dark)": "#0f1726" },
67
- surface: { "@media (prefers-color-scheme: dark)": "#131c2e" },
68
- surfaceHover: { "@media (prefers-color-scheme: dark)": "#1b2540" },
69
- border: { "@media (prefers-color-scheme: dark)": "#26324a" },
70
- ink: { "@media (prefers-color-scheme: dark)": "#e8eefc" },
71
- muted: { "@media (prefers-color-scheme: dark)": "#9fb0cc" },
72
- accent: { "@media (prefers-color-scheme: dark)": "#8f8bff" },
73
- accentHover: { "@media (prefers-color-scheme: dark)": "#a49fff" },
74
- accentInk: { "@media (prefers-color-scheme: dark)": "#0b1220" },
75
- accentSoft: { "@media (prefers-color-scheme: dark)": "#1e2547" },
76
- danger: { "@media (prefers-color-scheme: dark)": "#ff6b5e" },
77
- dangerHover: { "@media (prefers-color-scheme: dark)": "#ff8378" },
78
- dangerInk: { "@media (prefers-color-scheme: dark)": "#2a0b08" },
79
- dangerSoft: { "@media (prefers-color-scheme: dark)": "#2a1512" },
80
- focus: { "@media (prefers-color-scheme: dark)": "#7fb0ff" },
81
- scrim: { "@media (prefers-color-scheme: dark)": "rgba(2, 6, 16, 0.62)" },
82
- shadowCard: { "@media (prefers-color-scheme: dark)": "0 1px 2px rgba(0, 0, 0, 0.40)" },
83
- shadowPanel: { "@media (prefers-color-scheme: dark)": "0 16px 48px rgba(0, 0, 0, 0.55)" },
65
+ canvas: { "@media (prefers-color-scheme: dark)": "#141413" },
66
+ sunken: { "@media (prefers-color-scheme: dark)": "#0f0f0e" },
67
+ surface: { "@media (prefers-color-scheme: dark)": "#1c1c1a" },
68
+ surfaceHover: { "@media (prefers-color-scheme: dark)": "#262623" },
69
+ border: { "@media (prefers-color-scheme: dark)": "#363632" },
70
+ ink: { "@media (prefers-color-scheme: dark)": "#ecece7" },
71
+ muted: { "@media (prefers-color-scheme: dark)": "#a5a59e" },
72
+ accent: { "@media (prefers-color-scheme: dark)": "#8eaee8" },
73
+ accentHover: { "@media (prefers-color-scheme: dark)": "#a8c1ee" },
74
+ accentInk: { "@media (prefers-color-scheme: dark)": "#101318" },
75
+ accentSoft: { "@media (prefers-color-scheme: dark)": "#1f2838" },
76
+ danger: { "@media (prefers-color-scheme: dark)": "#ee8a80" },
77
+ dangerHover: { "@media (prefers-color-scheme: dark)": "#f3a49c" },
78
+ dangerInk: { "@media (prefers-color-scheme: dark)": "#1d0f0d" },
79
+ dangerSoft: { "@media (prefers-color-scheme: dark)": "#35201d" },
80
+ focus: { "@media (prefers-color-scheme: dark)": "#8eaee8" },
81
+ scrim: { "@media (prefers-color-scheme: dark)": "rgba(0, 0, 0, 0.6)" },
84
82
  });
85
83
 
86
84
  /**
@@ -90,23 +88,21 @@ export const ufAutoTheme = stylex.createTheme(ufTokens, {
90
88
  * preference a reader chose.
91
89
  */
92
90
  export const ufDarkTheme = stylex.createTheme(ufTokens, {
93
- canvas: "#0b1220",
94
- sunken: "#0f1726",
95
- surface: "#131c2e",
96
- surfaceHover: "#1b2540",
97
- border: "#26324a",
98
- ink: "#e8eefc",
99
- muted: "#9fb0cc",
100
- accent: "#8f8bff",
101
- accentHover: "#a49fff",
102
- accentInk: "#0b1220",
103
- accentSoft: "#1e2547",
104
- danger: "#ff6b5e",
105
- dangerHover: "#ff8378",
106
- dangerInk: "#2a0b08",
107
- dangerSoft: "#2a1512",
108
- focus: "#7fb0ff",
109
- scrim: "rgba(2, 6, 16, 0.62)",
110
- shadowCard: "0 1px 2px rgba(0, 0, 0, 0.40)",
111
- shadowPanel: "0 16px 48px rgba(0, 0, 0, 0.55)",
91
+ canvas: "#141413",
92
+ sunken: "#0f0f0e",
93
+ surface: "#1c1c1a",
94
+ surfaceHover: "#262623",
95
+ border: "#363632",
96
+ ink: "#ecece7",
97
+ muted: "#a5a59e",
98
+ accent: "#8eaee8",
99
+ accentHover: "#a8c1ee",
100
+ accentInk: "#101318",
101
+ accentSoft: "#1f2838",
102
+ danger: "#ee8a80",
103
+ dangerHover: "#f3a49c",
104
+ dangerInk: "#1d0f0d",
105
+ dangerSoft: "#35201d",
106
+ focus: "#8eaee8",
107
+ scrim: "rgba(0, 0, 0, 0.6)",
112
108
  });
package/tokens.stylex.js CHANGED
@@ -11,18 +11,37 @@
11
11
  // # Roles, not colours
12
12
  //
13
13
  // The names are what a token is *for*, never what it looks like. `accent` is
14
- // the colour a primary action wears; it is indigo today and a theme can make it
15
- // anything, and no rule in `preset.js` has to change when it does. A token
16
- // named `indigo500` would have made the preset unthemeable the moment someone
17
- // wanted a green product.
14
+ // the colour a primary action wears; it is a quiet cobalt today and a theme
15
+ // can make it anything, and no rule in `preset.js` has to change when it does.
16
+ // A token named `blue600` would have made the preset unthemeable the moment
17
+ // someone wanted a green product.
18
18
  //
19
- // The values are uf's identity, and `@uniflowed/brand` is where that identity
20
- // is decided: `accent`, `ink`, `canvas` and `muted` are brand's Indigo, Ink,
21
- // Mist and Slate, and the type, spacing and radius steps are brand's scales.
22
- // Brand cannot hold the token module itself — a StyleX token's name is computed
23
- // by the compiler, and `defineVars` takes literals, not an imported array — so
24
- // this module is brand's projection into the semantic roles a design system
25
- // needs and an identity does not have.
19
+ // # The palette
20
+ //
21
+ // `docs/ui-visual-language.md` explains it; in short: the interface is
22
+ // neutral first — warm greys with no blue cast, near-black ink on off-white
23
+ // paper — and colour is spent in one place, a single accent for what is
24
+ // selected, current or primary. Danger is a brick red rather than a signal
25
+ // red. Dark is designed as its own palette (charcoal, not navy; a lighter,
26
+ // desaturated accent that does not glow), not the light one inverted.
27
+ //
28
+ // The type and spacing steps are `@uniflowed/brand`'s scales. The colours and
29
+ // the corners are not: brand's cyan-to-magenta spectrum and its round corners
30
+ // are uf's logo and marketing identity, and an interface built from them looks
31
+ // like every other generated one, so this module's values are its own. Brand
32
+ // cannot hold the token module itself anyway — a StyleX token's name is
33
+ // computed by the compiler, and `defineVars` takes literals, not an imported
34
+ // array.
35
+ //
36
+ // # No shadows, no gradients
37
+ //
38
+ // The defaults draw depth with a rule and a change of surface, and nothing
39
+ // else. A shadow is the first thing a theme has to fight when the product's
40
+ // look is flat, and a gradient is a decision about a brand this module does
41
+ // not have, so neither is a token and neither appears in a default style.
42
+ // `crates/uf_stylex/src/tests/defaults.rs` compiles every default style —
43
+ // this module, `preset.js`, `theme.js` and every file in `registry/ui/` — and
44
+ // fails on a `box-shadow`, a `text-shadow`, a `drop-shadow()` or a gradient.
26
45
  //
27
46
  // # What is not a token
28
47
  //
@@ -45,31 +64,31 @@ import { stylex } from "@uniflowed/stylex";
45
64
  // `ThemeOverrides<typeof ufTokens>` reject a token this module does not declare.
46
65
  export const ufTokens = stylex.defineVars({
47
66
  // Surfaces, from furthest back to nearest front.
48
- canvas: "#f8fafc",
49
- sunken: "#eef2f7",
67
+ canvas: "#f6f6f4",
68
+ sunken: "#eeeeeb",
50
69
  surface: "#ffffff",
51
- surfaceHover: "#f1f5f9",
52
- border: "#dbe3ec",
70
+ surfaceHover: "#f1f1ee",
71
+ border: "#d9d9d4",
53
72
 
54
73
  // Text.
55
- ink: "#0f172a",
56
- muted: "#475569",
74
+ ink: "#1b1b19",
75
+ muted: "#5c5c57",
57
76
 
58
77
  // The colour a primary action wears, and what is legible on it.
59
- accent: "#5c49ff",
60
- accentHover: "#4a37f0",
78
+ accent: "#2b58b5",
79
+ accentHover: "#234996",
61
80
  accentInk: "#ffffff",
62
- accentSoft: "#eeecff",
81
+ accentSoft: "#e8eef9",
63
82
 
64
83
  // The colour a destructive action wears.
65
- danger: "#b42318",
66
- dangerHover: "#9a1c12",
84
+ danger: "#b1271d",
85
+ dangerHover: "#931f17",
67
86
  dangerInk: "#ffffff",
68
- dangerSoft: "#fef3f2",
87
+ dangerSoft: "#fbedeb",
69
88
 
70
89
  // Focus ring, and the wash behind a modal surface.
71
- focus: "#2677ff",
72
- scrim: "rgba(15, 23, 42, 0.48)",
90
+ focus: "#2b58b5",
91
+ scrim: "rgba(27, 27, 25, 0.45)",
73
92
 
74
93
  // Type.
75
94
  fontSans: "ui-sans-serif, system-ui, -apple-system, Segoe UI, Roboto, sans-serif",
@@ -95,20 +114,30 @@ export const ufTokens = stylex.defineVars({
95
114
  space8: "32px",
96
115
  space12: "48px",
97
116
 
98
- // Shape.
99
- radiusSm: "8px",
100
- radiusMd: "12px",
101
- radiusLg: "16px",
102
- radiusXl: "24px",
117
+ // Shape. Three small steps and a pill, and nothing rounder: a control is
118
+ // 2px, a button, field group or popover 4px, and the largest surface — a
119
+ // card, a dialog — 6px. `radiusPill` is for the few things that are round
120
+ // by what they are, such as a switch's track or a radio's dot, and not a
121
+ // way to soften a rectangle.
122
+ radiusSm: "2px",
123
+ radiusMd: "4px",
124
+ radiusLg: "6px",
103
125
  radiusPill: "999px",
104
126
 
105
127
  // The size of a control a finger or a pointer aims at.
106
128
  sizeControl: "18px",
107
129
 
108
- // Elevation and motion.
109
- shadowCard: "0 1px 2px rgba(15, 23, 42, 0.06), 0 1px 3px rgba(15, 23, 42, 0.10)",
110
- shadowPanel: "0 16px 48px rgba(15, 23, 42, 0.24)",
130
+ // Motion. Two short durations and one easing: `durationFast` for a colour
131
+ // or a chevron, `durationBase` for something that travels, such as a
132
+ // switch's thumb or a drawer. The easing decelerates and never overshoots,
133
+ // so nothing bounces. A transition names the properties it moves, never
134
+ // `all`, and is `0s` under `prefers-reduced-motion: reduce`;
135
+ // `crates/uf_stylex/src/tests/defaults.rs` holds every default style to
136
+ // that.
137
+ //
138
+ // There is no elevation either: a surface in front of another is told
139
+ // apart by a 1px `border` and its own background, never by a shadow.
111
140
  durationFast: "120ms",
112
- durationBase: "200ms",
141
+ durationBase: "160ms",
113
142
  easing: "cubic-bezier(0.2, 0, 0, 1)",
114
143
  });