@neo4j-ndl/react 4.20.0 → 4.20.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@neo4j-ndl/react",
3
- "version": "4.20.0",
3
+ "version": "4.20.1",
4
4
  "sideEffects": false,
5
5
  "description": "React implementation of Neo4j Design System",
6
6
  "keywords": [
@@ -38,8 +38,26 @@ Use Neo4j Needle components instead of hand-rolling equivalent UI.
38
38
  package's `.d.ts` type contract is authoritative: if the types disagree with
39
39
  these docs, the **types win**. Do not reverse-engineer behavior from the
40
40
  compiled implementation source in `node_modules`.
41
- - Pass native HTML attributes, `aria-*`, and `data-*` via the `htmlAttributes`
42
- prop, only where the component's doc says so.
41
+ - **Customize via component props first.** Needle components expose typed
42
+ props for spacing, layout, variant, and accessibility use those before
43
+ reaching for `className` or `htmlAttributes`. The escape-hatch order is:
44
+ 1. **Component props** (e.g. `gap="8"`, `variant="danger"`, `size="small"`) —
45
+ always preferred; type-checked and token-constrained.
46
+ 2. **`className`** — for utility classes the component doesn't cover (e.g.
47
+ `n-flex`, `n-items-center` on a plain element or when composing). Verify
48
+ every class against the stylesheet; see the class-names rule below.
49
+ 3. **`htmlAttributes`** — for native HTML attributes, `aria-*`, and `data-*`
50
+ the component doesn't expose as props. Valid reasons include:
51
+ - `id` — for `aria-controls` references, portal targets, or form label wiring
52
+ - `aria-*` — when no dedicated prop exists (e.g. `aria-haspopup`, `aria-expanded`
53
+ on a trigger, `aria-label` on a TextInput without a label)
54
+ - `data-*` — custom data attributes for testing or integration
55
+ - Native HTML attributes not surfaced as props (e.g. `type="email"`,
56
+ `type="password"`, `max`/`min`/`step` on a TextInput, `href` on
57
+ Typography as `as="a"`)
58
+ Do **not** use `htmlAttributes` to override behavior the component already
59
+ provides (e.g. setting `role="alertdialog"` on a Dialog — use `variant`
60
+ instead), or for styling — use component props or `className`.
43
61
  - Compound components are exposed as subcomponents (e.g. `Tooltip.Trigger`,
44
62
  `Tooltip.Content`). Follow each component's own doc — don't assume one
45
63
  component's compound shape matches another's.
@@ -61,15 +79,48 @@ Use Neo4j Needle components instead of hand-rolling equivalent UI.
61
79
  Tailwind-shaped guesses (`n-justify-between`, `n-items-baseline`) and
62
80
  near-misses on real scales (`n-gap-token-1` when the scale is 2/4/6/8/12/16)
63
81
  become invisible layout bugs, not errors. `Flex`, `Box` and `Typography` with
64
- token props sidestep the question entirely — prefer them.
82
+ token props sidestep the question entirely — prefer them. For the full token
83
+ list and class verification, see [references/styling.md](references/styling.md).
84
+ - **Tokens only.** Never hardcode `hex`, `rgb()`, `oklch()`, or arbitrary
85
+ Tailwind color values in generated code. All colors come from semantic theme
86
+ custom properties (`--theme-color-*`) that resolve to the correct light/dark
87
+ value automatically. See [references/styling.md](references/styling.md) for
88
+ the complete token catalog.
65
89
  - Follow the accessibility guidance in each component's doc; don't strip built-in
66
90
  roles or keyboard behavior.
67
91
 
92
+ ## Reuse before you create
93
+
94
+ Needle is a maintained design system. Before writing custom UI, check whether an
95
+ existing component already covers the need — you get the maintained component,
96
+ not a lookalike you now own.
97
+
98
+ 1. **Scan the component catalog below.** The table lists every shipped
99
+ component with its docs file and import path. If a Needle component covers
100
+ the need, use it instead of hand-rolling an equivalent.
101
+ 2. **Read the component's doc** (`components/<file>`) before using it — props,
102
+ compound shape, and accessibility behavior differ per component.
103
+ 3. **Create only when no component fits.** If the catalog doesn't cover it,
104
+ compose existing components (e.g. `Flex` + `Typography` + `Banner.Actions`)
105
+ rather than introducing a new component. Only create a new component when the
106
+ pattern is genuinely novel and no combination of existing primitives works.
107
+
108
+ ### Don't restyle a component into another
109
+
110
+ Each button variant is a distinct component with its own visual language.
111
+ Don't take a `FilledButton` and restyle it to look like an `OutlinedButton` —
112
+ use `OutlinedButton` directly. Same for tags (`Tag`, `SelectableTag`,
113
+ `DismissibleTag`, `ReadOnlyTag`), and any other variant family.
114
+
68
115
  ## How to use this skill
69
116
 
70
117
  1. Find the component in the catalog below to get its docs file and import path.
71
118
  2. Read `components/<file>` for that component's props, accessibility
72
119
  requirements, and runnable examples; prefer those examples.
120
+ 3. For styling — token names, utility class lists, prefix rules — consult
121
+ [references/styling.md](references/styling.md). It points at the shipped
122
+ stylesheet so class and token lists can't drift from what the package
123
+ actually ships.
73
124
 
74
125
  ## Components
75
126
 
@@ -0,0 +1,290 @@
1
+ # Styling reference
2
+
3
+ The stylesheet is the source of truth for which classes and custom properties
4
+ exist. **Never hardcode a class name or token value from memory** — verify it
5
+ against the shipped CSS first.
6
+
7
+ ## Where the stylesheet lives
8
+
9
+ ```
10
+ node_modules/@neo4j-ndl/base/lib/neo4j-ds-styles.css
11
+ ```
12
+
13
+ In the Needle repo source it is generated from `packages/base/src/` (Tailwind
14
+ preset + token JSON), but the built file above is what a consuming app ships.
15
+ When checking whether a class or custom property exists, grep the built file —
16
+ not the source `.pcss` — because Tailwind tree-shakes classes that no source
17
+ file uses.
18
+
19
+ ### Quick verification
20
+
21
+ ```sh
22
+ # Does a class exist?
23
+ CSS=node_modules/@neo4j-ndl/base/lib/neo4j-ds-styles.css
24
+ grep -qE '\.n-flex\b' "$CSS" && echo "exists" || echo "MISSING"
25
+
26
+ # Does a custom property exist?
27
+ grep -qE -- '--space-8:' "$CSS" && echo "exists" || echo "MISSING"
28
+ ```
29
+
30
+ ## Tokens only — never raw color values
31
+
32
+ Generated or hand-written code must **never** contain hardcoded `hex`, `rgb()`,
33
+ `oklch()`, or arbitrary Tailwind color values. All colors come from semantic
34
+ theme tokens that automatically resolve to the correct light/dark value:
35
+
36
+ ```css
37
+ /* ✅ Token — adapts to theme */
38
+ color: var(--theme-color-neutral-text-default);
39
+ background: var(--theme-color-primary-bg-weak);
40
+
41
+ /* ❌ Hardcoded — breaks dark mode, drifts from the system */
42
+ color: #1a1b1d;
43
+ background: rgb(10 97 144);
44
+ ```
45
+
46
+ ### Semantic color families
47
+
48
+ Each family has the same suffix set (`text`, `icon`, `bg-weak`, `bg-strong`,
49
+ `bg-status`, `border-strong`, `border-weak`, and for primary/danger also
50
+ `hover-*`, `pressed-*`):
51
+
52
+ | Family | CSS custom property prefix | Utility class prefix |
53
+ |--------|---------------------------|---------------------|
54
+ | Neutral | `--theme-color-neutral-*` | `n-text-neutral-*`, `n-bg-neutral-*`, `n-border-neutral-*` |
55
+ | Primary | `--theme-color-primary-*` | `n-text-primary-*`, `n-bg-primary-*`, `n-border-primary-*` |
56
+ | Danger | `--theme-color-danger-*` | `n-text-danger-*`, `n-bg-danger-*`, `n-border-danger-*` |
57
+ | Warning | `--theme-color-warning-*` | `n-text-warning-*`, `n-bg-warning-*`, `n-border-warning-*` |
58
+ | Success | `--theme-color-success-*` | `n-text-success-*`, `n-bg-success-*`, `n-border-success-*` |
59
+ | Discovery | `--theme-color-discovery-*` | `n-text-discovery-*`, `n-bg-discovery-*`, `n-border-discovery-*` |
60
+
61
+ **Do not use `n-bg-dark-*` / `n-bg-light-*` prefixed classes.** They pin a
62
+ specific theme and break when the user switches. The non-prefixed classes
63
+ above resolve to the active theme automatically.
64
+
65
+ ### Space tokens
66
+
67
+ | Token | Value | Utility class (gap/padding) |
68
+ |-------|-------|-----|
69
+ | `--space-2` | 2px | `n-gap-token-2` |
70
+ | `--space-4` | 4px | `n-gap-token-4` |
71
+ | `--space-6` | 6px | `n-gap-token-6` |
72
+ | `--space-8` | 8px | `n-gap-token-8` |
73
+ | `--space-12` | 12px | `n-gap-token-12` |
74
+ | `--space-16` | 16px | `n-gap-token-16` |
75
+ | `--space-20` | 20px | `n-gap-token-20` (not in the prebuilt CSS) |
76
+ | `--space-24` | 24px | `n-gap-token-24` (not in the prebuilt CSS) |
77
+ | `--space-32` | 32px | `n-gap-token-32` |
78
+ | `--space-48` | 48px | `n-gap-token-48` |
79
+ | `--space-64` | 64px | `n-gap-token-64` |
80
+
81
+ ### The same number means different things in the two systems
82
+
83
+ This is the easiest way to get spacing silently wrong, and it is invisible to
84
+ `tsc`, to the build and to review:
85
+
86
+ | What you write | Renders |
87
+ |----------------|---------|
88
+ | `<Flex gap="16">` — component prop | **16px** |
89
+ | `className="gap-token-16"` — token-named utility | **16px** |
90
+ | `className="gap-16"` — Tailwind numeric utility | **64px** |
91
+
92
+ A Needle spacing prop takes a **token name that is its pixel value**. A Tailwind
93
+ numeric utility takes a **step on Tailwind's 4px scale**. So `gap="8"` is 8px
94
+ while `gap-8` is 32px, and the two scales never coincide at any step — mixing
95
+ them multiplies the spacing you meant by four.
96
+
97
+ Two gap scales are **defined in the preset**: the token-named one
98
+ (`n-gap-token-2` … `n-gap-token-64`) and Tailwind's numeric one (`n-gap-1`,
99
+ `n-gap-1.5`, `n-gap-2`, `n-gap-3` …). Both are valid wherever Tailwind is
100
+ generating classes. Only a handful of each are present in the prebuilt
101
+ stylesheet — see [which classes work in your app](#whether-a-utility-class-works-is-a-property-of-the-app-not-the-class)
102
+ before relying on any specific one. `Flex` and `Box` accept token values
103
+ directly (`gap="8"`, `padding="12"`) and sidestep the question entirely, which
104
+ is why they are preferred.
105
+
106
+ ### Border-radius tokens
107
+
108
+ | Token | Value |
109
+ |-------|-------|
110
+ | `--border-radius-none` | 0 |
111
+ | `--border-radius-sm` | 4px |
112
+ | `--border-radius-md` | 6px |
113
+ | `--border-radius-lg` | 8px |
114
+ | `--border-radius-xl` | 12px |
115
+ | `--border-radius-2xl` | 16px |
116
+ | `--border-radius-3xl` | 24px |
117
+ | `--border-radius-full` | 9999px |
118
+
119
+ `Box` and `Flex` accept `borderRadius="xl"` directly. Utility classes
120
+ `n-rounded-sm/md/lg/xl/2xl` also exist.
121
+
122
+ ### Shadow tokens
123
+
124
+ | Token | Utility class |
125
+ |-------|---------------|
126
+ | `--theme-shadow-raised` | `n-shadow-raised` |
127
+ | `--theme-shadow-overlay` | `n-shadow-overlay` |
128
+
129
+ ### JS token imports
130
+
131
+ For inline styles or JS logic, import the token object:
132
+
133
+ ```tsx
134
+ import { tokens } from '@neo4j-ndl/base';
135
+
136
+ tokens.space['8'] // '8px'
137
+ tokens.borderRadius.xl // '12px'
138
+ tokens.colors.primary['500'] // raw palette value
139
+ ```
140
+
141
+ Prefer CSS custom properties in `style` over JS token values where possible —
142
+ `var(--theme-color-*)` adapts to theme, a JS palette value does not.
143
+
144
+ ## Whether a utility class works is a property of the app, not the class
145
+
146
+ Two independent questions decide it, and the app's `tailwind.config` answers
147
+ both. Read it before writing a single class.
148
+
149
+ **1. Does the app run its own Tailwind build?**
150
+
151
+ | | What works |
152
+ |---|-----------|
153
+ | **Yes** — it consumes the Needle preset | Any standard Tailwind utility, generated on demand: `sr-only`, `p-token-6`, `gap-3` and the rest |
154
+ | **No** — it only imports `@neo4j-ndl/base/lib/neo4j-ds-styles.css` | Only the classes already present in that file. Everything else silently does nothing |
155
+
156
+ **2. What prefix does it use?**
157
+
158
+ The Needle preset sets `prefix: 'n-'`, but an app can override it and the app's
159
+ config wins. The Aura console monorepo sets `prefix: ''`, so utilities there are
160
+ written unprefixed — `flex`, `gap-4`, not `n-flex`. An app with no Tailwind build
161
+ at all gets only the prebuilt stylesheet's classes, and those are `n-` prefixed.
162
+
163
+ Getting this wrong is invisible: a class at the wrong prefix compiles, passes
164
+ type-checking and renders nothing. And it can look fine by accident — in a
165
+ `prefix: ''` app that also imports the prebuilt CSS, `n-flex` still renders
166
+ because it happens to be baked in, while `n-items-baseline` does not.
167
+
168
+ The prebuilt stylesheet is a **by-product of building Needle, not a utility
169
+ library.** Its production build scans only Needle's own component source
170
+ (`packages/react/src`, excluding stories and Playwright specs), so the utilities
171
+ in it are whatever those components happened to use. Nothing curates that set for
172
+ consumers, and it shifts whenever component source changes.
173
+
174
+ This is also why a class can work in Needle's Storybook and fail in a consuming
175
+ app: the non-production build widens the scan to include
176
+ `packages/storybook/src`, so writing a class in a story generates it. `n-sr-only`
177
+ behaves exactly this way — it is used in Needle's own `.pcss` via
178
+ `@apply n-sr-only`, which **inlines the declarations** into the component's rule
179
+ rather than emitting a reusable `.n-sr-only` selector. It works in Storybook and
180
+ is absent from the published CSS.
181
+
182
+ If there is no `tailwind.config` in the app, treat the prebuilt stylesheet as the
183
+ complete set and grep it.
184
+
185
+ An app that wants the full utility set opts into the first setup by consuming the
186
+ preset, which `@neo4j-ndl/base` exports from its main entry:
187
+
188
+ ```js
189
+ // tailwind.config.js
190
+ import { tailwindConfig } from '@neo4j-ndl/base';
191
+
192
+ export default {
193
+ presets: [tailwindConfig],
194
+ content: ['./src/**/*.{ts,tsx}'],
195
+ };
196
+ ```
197
+
198
+ ### The prebuilt set changes between releases
199
+
200
+ Because it is a by-product of component source, it moves without anyone
201
+ curating it. Read the
202
+ version from `node_modules/@neo4j-ndl/base/package.json` and grep *that* copy of
203
+ the stylesheet; never a copy from elsewhere on disk, and never this document
204
+ alone.
205
+
206
+ ### Two things that are fictional in both setups
207
+
208
+ Everything above is a real utility somewhere. These are not — they are invented
209
+ namespaces that resolve to nothing no matter how the app is built, and both have
210
+ been written by generated code more than once:
211
+
212
+ - **`--theme-palette-*` is not a namespace.** The semantic one is
213
+ `--theme-color-*`. There are zero occurrences of `--theme-palette` in the
214
+ package.
215
+ - **Radius is not under `--theme-*`.** The tokens are `--border-radius-sm|md|lg|
216
+ xl|2xl|3xl|full|none`, so `--theme-radius-lg` is nothing.
217
+
218
+ A `var()` with no fallback renders nothing when the property is undefined, so
219
+ both fail silently rather than loudly.
220
+
221
+ ## Layout utility classes present in the prebuilt stylesheet
222
+
223
+ **This section matters only in an app with no Tailwind build of its own** — where
224
+ the prebuilt stylesheet is the complete set. With a Tailwind build, any valid
225
+ utility generates and the lists below do not constrain you.
226
+
227
+ The right-hand column is **not** a list of invalid class names — every one is a
228
+ real Tailwind utility the preset can generate. They are simply absent from the
229
+ prebuilt `neo4j-ds-styles.css`. Names are `n-` prefixed here because that is the
230
+ prefix the shipped stylesheet was built with, whatever prefix your own build
231
+ uses. Use them only when a component prop isn't available: `Flex`,
232
+ `Box` and `Typography` cover most layout needs with type-safe token props.
233
+
234
+ | Purpose | In the prebuilt CSS | Valid, but NOT in the prebuilt CSS |
235
+ |---------|-------------------|--------------------------|
236
+ | Flexbox | `n-flex`, `n-flex-row`, `n-flex-col`, `n-flex-1`, `n-flex-shrink-0` | — |
237
+ | Align items | `n-items-start`, `n-items-end`, `n-items-center` | `n-items-baseline`, `n-items-stretch` |
238
+ | Justify content | `n-justify-start`, `n-justify-end`, `n-justify-center`, `n-justify-between`, `n-justify-items-center` | `n-justify-around`, `n-justify-evenly` |
239
+ | Gap | `n-gap-1`, `n-gap-2`, `n-gap-4`, `n-gap-6`, `n-gap-9`, `n-gap-12`; `n-gap-token-2/4/6/8/12/16/32/48/64` | `n-gap-1.5`, `n-gap-3`, `n-gap-5`, `n-gap-8`, `n-gap-16`, `n-gap-token-20`, `n-gap-token-24` |
240
+ | Width | `n-w-full`, `n-w-fit`, `n-w-max`, `n-w-token-32` | `n-w-1/2` and other fractional widths |
241
+ | Padding | `n-p-0`, `n-p-2`, `n-p-3`, `n-p-4`, `n-p-8`, `n-p-14` | `n-p-12`, and the whole `n-p-token-*` scale — prefer `Box padding="12"` |
242
+ | Text align | `n-text-left`, `n-text-center` | `n-text-right` |
243
+
244
+ **A class in the right-hand column compiles and renders nothing in a
245
+ prebuilt-CSS app** — a silent layout bug, invisible to `tsc` and to review.
246
+ Prefer a component prop, or an inline style with a real token.
247
+
248
+ ## How to verify before writing
249
+
250
+ 1. **Check component props first.** `Flex`, `Box`, `Typography` accept typed
251
+ token values that are always correct — prefer them over utility classes or
252
+ `htmlAttributes`. See the customization hierarchy below.
253
+ 2. **Check the stylesheet**: grep `neo4j-ds-styles.css` for the exact class
254
+ or property.
255
+ 3. **Check the consuming app's Tailwind config** for `prefix` if you're unsure
256
+ whether `n-` or bare classes are in use.
257
+ 4. **Never copy a class from another library** (MUI, Radix, Chakra, plain
258
+ Tailwind). They won't exist in the Needle stylesheet.
259
+
260
+ ## Customization hierarchy
261
+
262
+ When adjusting a Needle component, work through these in order. Each level is
263
+ more error-prone than the one before it:
264
+
265
+ 1. **Component props** — typed, token-constrained, and type-checked. Always the
266
+ first choice. Examples: `gap="8"`, `padding="12"`, `borderRadius="xl"`,
267
+ `variant="danger"`, `size="small"`, `isFluid`, `ariaLabel`.
268
+
269
+ 2. **`className`** — for utility classes the component doesn't cover (e.g.
270
+ `n-flex`, `n-items-center` on a plain element, or when composing). These are
271
+ unchecked strings: verify every class against
272
+ `node_modules/@neo4j-ndl/base/lib/neo4j-ds-styles.css`. A wrong class compiles
273
+ and silently renders nothing.
274
+
275
+ 3. **`htmlAttributes`** — escape hatch for native HTML attributes, `aria-*`, and
276
+ `data-*` the component doesn't expose as props. Valid reasons, drawn from the
277
+ component docs:
278
+
279
+ - `id` — for `aria-controls` references, portal targets, or form label wiring
280
+ - `aria-*` — when no dedicated prop exists (e.g. `aria-haspopup` or
281
+ `aria-expanded` on a trigger button, `aria-label` on a `TextInput` without
282
+ a label)
283
+ - `data-*` — custom data attributes for testing or integration
284
+ - Native HTML attributes not surfaced as props (e.g. `type="email"`,
285
+ `type="password"`, `max`/`min`/`step` on a `TextInput`, `href` on
286
+ `Typography` with `as="a"`, `title` on an icon button)
287
+
288
+ Do **not** use `htmlAttributes` to override behavior the component already
289
+ provides — e.g. setting `role="alertdialog"` on a `Dialog` (use `variant`
290
+ instead) — or for styling (use component props or `className`).