@duro-app/ui 5.19.0 → 5.21.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/dist/table.js CHANGED
@@ -1,4 +1,4 @@
1
- import { M as F, q as gt, R as it, H as kt, t as pt, v as bt, w as ft, x as mt, b as ht, g as yt, r as St, y as Tt } from "./Table-DnaoH-h5.js";
1
+ import { M as F, q as gt, R as it, H as kt, t as pt, v as bt, w as ft, x as mt, b as ht, g as yt, r as St, y as Tt } from "./Table-Cw1B14R3.js";
2
2
  import { jsx as u, jsxs as q } from "react/jsx-runtime";
3
3
  import { c as H, m as p, d as b } from "./compiler-runtime-BuugS4D0.js";
4
4
  import { flexRender as E, useReactTable as rt, getPaginationRowModel as Ct, getFilteredRowModel as vt, getSortedRowModel as at, getCoreRowModel as st } from "@tanstack/react-table";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@duro-app/ui",
3
- "version": "5.19.0",
3
+ "version": "5.21.0",
4
4
  "type": "module",
5
5
  "license": "MIT",
6
6
  "repository": {
@@ -92,7 +92,7 @@
92
92
  "dependencies": {
93
93
  "@tanstack/react-virtual": "^3.14.13",
94
94
  "qrcode": "^1.5.4",
95
- "@duro-app/tokens": "^5.19.0"
95
+ "@duro-app/tokens": "^5.21.0"
96
96
  },
97
97
  "devDependencies": {
98
98
  "@babel/preset-typescript": "^7.28.0",
@@ -2,11 +2,12 @@ import type {ComponentMeta} from '../component-meta'
2
2
 
3
3
  export const meta: ComponentMeta = {
4
4
  description:
5
- 'Semantic heading element (h1-h6) with typography presets. Always use the correct level for document hierarchy. Visual size can differ from semantic level via the variant prop.',
5
+ 'Semantic heading element (h1-h6) with typography presets. Always use the correct level for document hierarchy. The look is `variant` (heading, display, overline) and its size is `size` (sm, md, lg, xl), both independent of the semantic level. The old combined variants (headingSm, displayLg, …) still work and are deprecated.',
6
6
  whenToUse: [
7
7
  'Page titles (level 1)',
8
8
  'Section headers (level 2-3)',
9
9
  'Card or panel titles (level 3-4)',
10
+ 'Group titles over a list of rows or a form section (level 3-4, variant="overline", any size)',
10
11
  ],
11
12
  whenNotToUse: [
12
13
  'Body text — use Text',
@@ -22,7 +23,9 @@ export const meta: ComponentMeta = {
22
23
  ],
23
24
  example: `<Stack gap="md">
24
25
  <Heading level={1}>Page Title</Heading>
25
- <Heading level={2} variant="headingMd">Section</Heading>
26
- <Heading level={3}>Subsection</Heading>
26
+ <Heading level={2} size="md">Section</Heading>
27
+ <Heading level={1} variant="display" size="lg">Hero</Heading>
28
+ <Heading level={3} variant="overline" color="muted">Group</Heading>
29
+ <Heading level={2} variant="overline" size="lg">Large group</Heading>
27
30
  </Stack>`,
28
31
  }
@@ -49,3 +49,48 @@ export const WithId: Story = {
49
49
  await expect(canvas.getByRole('link', {name: 'Install'})).toHaveAttribute('href', '#install')
50
50
  },
51
51
  }
52
+
53
+ /** `variant` is the look and `size` its size, independent of the level: a group title over a
54
+ * list of rows is an `overline` heading, at any size. */
55
+ export const Overline: Story = {
56
+ render: () => (
57
+ <Stack gap="xs">
58
+ <Heading level={3} variant="overline" color="muted">
59
+ Today
60
+ </Heading>
61
+ <Heading level={3} variant="overline" size="md" color="muted">
62
+ Earlier
63
+ </Heading>
64
+ <Heading level={2} variant="overline" size="lg">
65
+ Archive
66
+ </Heading>
67
+ </Stack>
68
+ ),
69
+ play: async ({canvas}) => {
70
+ await expect(canvas.getByRole('heading', {level: 3, name: 'Today'})).toBeInTheDocument()
71
+ await expect(canvas.getAllByRole('heading', {level: 3})).toHaveLength(2)
72
+ const sm = getComputedStyle(canvas.getByRole('heading', {name: 'Today'})).fontSize
73
+ const lg = getComputedStyle(canvas.getByRole('heading', {name: 'Archive'})).fontSize
74
+ await expect(parseFloat(lg)).toBeGreaterThan(parseFloat(sm))
75
+ },
76
+ }
77
+
78
+ /** The old combined variants still render the same preset as the two props. */
79
+ export const LegacyVariant: Story = {
80
+ render: () => (
81
+ <Stack gap="xs">
82
+ <Heading level={2} variant="headingSm">
83
+ Legacy
84
+ </Heading>
85
+ <Heading level={2} variant="heading" size="sm">
86
+ Current
87
+ </Heading>
88
+ </Stack>
89
+ ),
90
+ play: async ({canvas}) => {
91
+ const legacy = getComputedStyle(canvas.getByRole('heading', {name: 'Legacy'}))
92
+ const current = getComputedStyle(canvas.getByRole('heading', {name: 'Current'}))
93
+ await expect(legacy.fontSize).toBe(current.fontSize)
94
+ await expect(legacy.fontWeight).toBe(current.fontWeight)
95
+ },
96
+ }
@@ -3,7 +3,12 @@ import {html} from 'react-strict-dom'
3
3
  import {typePresets} from '@duro-app/tokens/tokens/type-presets.css'
4
4
  import {styles} from './styles.css'
5
5
 
6
- export type HeadingVariant =
6
+ /** The heading's look; its size is the `size` prop. */
7
+ export type HeadingFamily = 'heading' | 'display' | 'overline'
8
+ export type HeadingSize = 'sm' | 'md' | 'lg' | 'xl'
9
+
10
+ /** @deprecated Use `variant` with `size`: `headingSm` is `variant="heading" size="sm"`. */
11
+ export type HeadingLegacyVariant =
7
12
  | 'displayLg'
8
13
  | 'displayMd'
9
14
  | 'displaySm'
@@ -12,9 +17,14 @@ export type HeadingVariant =
12
17
  | 'headingMd'
13
18
  | 'headingSm'
14
19
 
20
+ export type HeadingVariant = HeadingFamily | HeadingLegacyVariant
21
+
15
22
  interface HeadingProps {
16
23
  level: 1 | 2 | 3 | 4 | 5 | 6
24
+ /** The look: `heading` (default), `display` (fluid hero titles), `overline` (small uppercase group titles). */
17
25
  variant?: HeadingVariant
26
+ /** The size within the look. Default: the level's size for `heading`, `md` for `display`, `sm` for `overline`; `display` and `overline` stop at `lg`. */
27
+ size?: HeadingSize
18
28
  color?: 'default' | 'muted' | 'accent'
19
29
  align?: 'start' | 'center' | 'end'
20
30
  /** The heading's id: an in-page link's target (`href="#id"`), an `aria-labelledby` reference. */
@@ -22,13 +32,32 @@ interface HeadingProps {
22
32
  children: ReactNode
23
33
  }
24
34
 
25
- const defaultVariantMap: Record<number, HeadingVariant> = {
26
- 1: 'headingXl',
27
- 2: 'headingLg',
28
- 3: 'headingMd',
29
- 4: 'headingSm',
30
- 5: 'headingSm',
31
- 6: 'headingSm',
35
+ type Preset = keyof typeof typePresets
36
+
37
+ const levelSize: Record<HeadingProps['level'], HeadingSize> = {
38
+ 1: 'xl',
39
+ 2: 'lg',
40
+ 3: 'md',
41
+ 4: 'sm',
42
+ 5: 'sm',
43
+ 6: 'sm',
44
+ }
45
+
46
+ const presets = {
47
+ heading: {sm: 'headingSm', md: 'headingMd', lg: 'headingLg', xl: 'headingXl'},
48
+ display: {sm: 'displaySm', md: 'displayMd', lg: 'displayLg'},
49
+ overline: {sm: 'overline', md: 'overlineMd', lg: 'overlineLg'},
50
+ } as const satisfies Record<HeadingFamily, Partial<Record<HeadingSize, Preset>>>
51
+
52
+ function presetOf(
53
+ level: HeadingProps['level'],
54
+ variant: HeadingVariant = 'heading',
55
+ size?: HeadingSize,
56
+ ): Preset {
57
+ if (variant === 'heading') return presets.heading[size ?? levelSize[level]]
58
+ if (variant === 'display') return presets.display[size === 'xl' ? 'lg' : (size ?? 'md')]
59
+ if (variant === 'overline') return presets.overline[size === 'xl' ? 'lg' : (size ?? 'sm')]
60
+ return variant
32
61
  }
33
62
 
34
63
  const alignMap = {
@@ -37,9 +66,20 @@ const alignMap = {
37
66
  end: styles.alignEnd,
38
67
  } as const
39
68
 
40
- export function Heading({level, variant, color = 'default', align, id, children}: HeadingProps) {
41
- const resolvedVariant = variant ?? defaultVariantMap[level]
42
- const style = [typePresets[resolvedVariant], styles[color], align && alignMap[align]]
69
+ export function Heading({
70
+ level,
71
+ variant,
72
+ size,
73
+ color = 'default',
74
+ align,
75
+ id,
76
+ children,
77
+ }: HeadingProps) {
78
+ const style = [
79
+ typePresets[presetOf(level, variant, size)],
80
+ styles[color],
81
+ align && alignMap[align],
82
+ ]
43
83
 
44
84
  if (level === 1)
45
85
  return (
@@ -6,10 +6,10 @@ export const meta: ComponentMeta = {
6
6
  whenToUse: [
7
7
  'Body text (paragraphs, descriptions)',
8
8
  'UI labels and metadata',
9
- 'Captions, code snippets, overlines',
9
+ 'Captions, code snippets, and overline eyebrows above a Heading',
10
10
  ],
11
11
  whenNotToUse: [
12
- 'Page or section titles — use Heading',
12
+ 'Page, section or group titles — use Heading, even in overline style (variant="overline")',
13
13
  'Form field labels — use Field.Label (auto-binds to form)',
14
14
  ],
15
15
  relatedTo: [
@@ -55,12 +55,15 @@ Presets compose scale tokens + weight + tracking into named styles via `typePres
55
55
 
56
56
  ### Heading
57
57
 
58
- | Preset | Size | Weight | Tracking |
59
- | ----------- | ----- | -------- | -------- |
60
- | `headingSm` | 20/28 | Semibold | Tight |
61
- | `headingMd` | 24/32 | Semibold | Tight |
62
- | `headingLg` | 30/36 | Bold | Tight |
63
- | `headingXl` | 36/44 | Bold | Tight |
58
+ | Preset | Size | Weight | Tracking |
59
+ | ----------- | ----- | -------- | -------------------------------------------------------- |
60
+ | `headingSm` | 20/28 | Semibold | Tight |
61
+ | `headingMd` | 24/32 | Semibold | Tight |
62
+ | `headingLg` | 30/36 | Bold | Tight |
63
+ | `headingXl` | 36/44 | Bold | Tight |
64
+ | `overline` | 12/16 | Semibold | Wide, uppercase (`overlineMd` 14/20, `overlineLg` 16/24) |
65
+
66
+ `Heading` picks a preset from two props: `variant` (`heading`, `display`, `overline`) and `size` (`sm`, `md`, `lg`, `xl`). `<Heading level={3} variant="overline" size="lg">` is a group title at 16/24. Without `size`, a heading takes its level's size, a display title `md` and an overline `sm`.
64
67
 
65
68
  ### Display (fluid)
66
69
 
@@ -82,12 +85,13 @@ See the **Fluid Display** story for a resizable demo.
82
85
 
83
86
  ## When to use which layer
84
87
 
85
- | Scenario | Use |
86
- | ---------------------------------------------- | ----------------------------------------- |
87
- | Body text in a page or card | `<Text variant="bodyMd">` |
88
- | Section heading | `<Heading level={2}>` |
89
- | Hero or landing page title | `<Heading level={1} variant="displayLg">` |
90
- | Form label | `<Text variant="label">` |
91
- | Inline code | `<Text variant="code">` |
92
- | Custom component needing raw font size | `typeScale.fontSize4` token |
93
- | Composing a one-off style with preset defaults | `typePresets.bodyMd` in `css.create()` |
88
+ | Scenario | Use |
89
+ | ---------------------------------------------- | ------------------------------------------------- |
90
+ | Body text in a page or card | `<Text variant="bodyMd">` |
91
+ | Section heading | `<Heading level={2}>` |
92
+ | Group title over a list of rows | `<Heading level={3} variant="overline">` |
93
+ | Hero or landing page title | `<Heading level={1} variant="display" size="lg">` |
94
+ | Form label | `<Text variant="label">` |
95
+ | Inline code | `<Text variant="code">` |
96
+ | Custom component needing raw font size | `typeScale.fontSize4` token |
97
+ | Composing a one-off style with preset defaults | `typePresets.bodyMd` in `css.create()` |
@@ -20,7 +20,7 @@ export {Badge, type BadgeVariant, type BadgeSize} from './components/Badge/Badge
20
20
  export {Button, type ButtonVariant, type ButtonSize} from './components/Button/Button'
21
21
  export {Card, type CardVariant, type CardSize} from './components/Card/Card'
22
22
  export {Checkbox} from './components/Checkbox/Checkbox'
23
- export {Heading, type HeadingVariant} from './components/Heading/Heading'
23
+ export {Heading, type HeadingSize, type HeadingVariant} from './components/Heading/Heading'
24
24
  export {Input, type InputVariant} from './components/Input/Input'
25
25
  export {Switch} from './components/Switch/Switch'
26
26
  export {Text, type TextVariant, type TextColor} from './components/Text/Text'
package/src/index.ts CHANGED
@@ -56,7 +56,7 @@ export {
56
56
  type SnapPoint,
57
57
  } from './components/Drawer/Drawer'
58
58
  export {EmptyState} from './components/EmptyState/EmptyState'
59
- export {Heading, type HeadingVariant} from './components/Heading/Heading'
59
+ export {Heading, type HeadingSize, type HeadingVariant} from './components/Heading/Heading'
60
60
  export {List, type ListSelectionMode} from './components/List/List'
61
61
  export {LiveRegion, type LiveRegionPoliteness} from './components/LiveRegion/LiveRegion'
62
62
  export {Field} from './components/Field/Field'