@roadlittledawn/docs-design-system-react 0.9.1 → 0.11.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.
@@ -0,0 +1,141 @@
1
+ var __assign = (this && this.__assign) || function () {
2
+ __assign = Object.assign || function(t) {
3
+ for (var s, i = 1, n = arguments.length; i < n; i++) {
4
+ s = arguments[i];
5
+ for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p))
6
+ t[p] = s[p];
7
+ }
8
+ return t;
9
+ };
10
+ return __assign.apply(this, arguments);
11
+ };
12
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
13
+ import { Icon } from './Icon';
14
+ // ─── Sample SVG components used in stories ────────────────────────────────────
15
+ // Note: these sample SVG components intentionally omit accessibility attributes
16
+ // (aria-hidden, role, aria-label). The Icon component applies them based on
17
+ // whether an aria-label prop is provided.
18
+ var StarIcon = function (props) { return (_jsx("svg", __assign({ viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round" }, props, { children: _jsx("polygon", { points: "12 2 15.09 8.26 22 9.27 17 14.14 18.18 21.02 12 17.77 5.82 21.02 7 14.14 2 9.27 8.91 8.26 12 2" }) }))); };
19
+ var ChevronIcon = function (props) { return (_jsx("svg", __assign({ viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round" }, props, { children: _jsx("polyline", { points: "6 9 12 15 18 9" }) }))); };
20
+ var ExternalLinkIcon = function (props) { return (_jsxs("svg", __assign({ viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round" }, props, { children: [_jsx("path", { d: "M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6" }), _jsx("polyline", { points: "15 3 21 3 21 9" }), _jsx("line", { x1: "10", y1: "14", x2: "21", y2: "3" })] }))); };
21
+ // Raw SVG string example
22
+ var closeIconSvg = "<svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><line x1=\"18\" y1=\"6\" x2=\"6\" y2=\"18\"/><line x1=\"6\" y1=\"6\" x2=\"18\" y2=\"18\"/></svg>";
23
+ // ─── Meta ─────────────────────────────────────────────────────────────────────
24
+ var meta = {
25
+ title: 'Components/Icon',
26
+ component: Icon,
27
+ tags: ['autodocs'],
28
+ decorators: [
29
+ function (Story) { return (_jsx("div", { style: { color: 'var(--dds-tabs-panel-text)' }, children: _jsx(Story, {}) })); },
30
+ ],
31
+ argTypes: {
32
+ svg: {
33
+ description: 'SVG to render — accepts a React SVG component or a raw SVG string.',
34
+ control: false,
35
+ },
36
+ size: {
37
+ control: { type: 'number', min: 8, max: 64, step: 2 },
38
+ description: 'Width and height in pixels.',
39
+ table: { defaultValue: { summary: '16' } },
40
+ },
41
+ className: {
42
+ control: 'text',
43
+ description: 'Additional CSS class names.',
44
+ table: { defaultValue: { summary: '""' } },
45
+ },
46
+ 'aria-label': {
47
+ control: 'text',
48
+ description: 'Accessible label. When provided the icon is announced by screen readers. When omitted the icon is treated as decorative and hidden from assistive technology.',
49
+ },
50
+ },
51
+ parameters: {
52
+ docs: {
53
+ description: {
54
+ component: "\nThe `Icon` component renders an SVG icon without bundling any specific icon library.\nYou bring your own SVGs \u2014 as a React component or a raw SVG string \u2014 so you are never\nforced to adopt a particular icon set.\n\n## When to Use\n\n- Rendering icons imported with SVGR (e.g. `import { ReactComponent as Star } from './star.svg'`)\n- Rendering inline SVG components defined directly in your code\n- Rendering a trusted raw SVG string from your project's assets\n\n## When Not to Use\n\n- Do not pass untrusted or user-supplied SVG strings; the raw string path uses\n `dangerouslySetInnerHTML` without sanitization.\n- When you only need a decorative shape that can be achieved with CSS alone.\n\n## Accessibility\n\n- Decorative icons (no `aria-label`) automatically receive `aria-hidden=\"true\"` so\n they are ignored by screen readers.\n- Meaningful icons (e.g. a standalone icon button) should receive a descriptive\n `aria-label` so they are announced correctly.\n ",
55
+ },
56
+ },
57
+ },
58
+ };
59
+ export default meta;
60
+ // ─── Stories ──────────────────────────────────────────────────────────────────
61
+ /**
62
+ * Pass any React SVG component to the `svg` prop. The component is rendered
63
+ * directly with the provided `size` applied as `width` and `height`.
64
+ */
65
+ export var WithReactComponent = {
66
+ args: {
67
+ svg: StarIcon,
68
+ size: 24,
69
+ },
70
+ parameters: {
71
+ docs: {
72
+ source: {
73
+ code: "const StarIcon = (props: React.SVGProps<SVGSVGElement>) => (\n <svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"2\" strokeLinecap=\"round\" strokeLinejoin=\"round\" {...props}>\n <polygon points=\"12 2 15.09 8.26 22 9.27 17 14.14 18.18 21.02 12 17.77 5.82 21.02 7 14.14 2 9.27 8.91 8.26 12 2\" />\n </svg>\n);\n\n<Icon svg={StarIcon} size={24} />",
74
+ },
75
+ },
76
+ },
77
+ };
78
+ /**
79
+ * Pass a raw SVG string to the `svg` prop. The string is injected using
80
+ * `dangerouslySetInnerHTML` — ensure the content is from a trusted source.
81
+ */
82
+ export var WithSvgString = {
83
+ args: {
84
+ svg: closeIconSvg,
85
+ size: 24,
86
+ },
87
+ parameters: {
88
+ docs: {
89
+ source: {
90
+ code: "const closeIconSvg = `<svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><line x1=\"18\" y1=\"6\" x2=\"6\" y2=\"18\"/><line x1=\"6\" y1=\"6\" x2=\"18\" y2=\"18\"/></svg>`;\n\n<Icon svg={closeIconSvg} size={24} />",
91
+ },
92
+ },
93
+ },
94
+ };
95
+ /**
96
+ * When an `aria-label` is provided the icon receives `role="img"` and is
97
+ * announced by screen readers with the given label. Use this when the icon
98
+ * conveys meaning without surrounding text.
99
+ */
100
+ export var WithAriaLabel = {
101
+ args: {
102
+ svg: ExternalLinkIcon,
103
+ size: 20,
104
+ 'aria-label': 'Opens in a new tab',
105
+ },
106
+ parameters: {
107
+ docs: {
108
+ source: {
109
+ code: "<Icon svg={ExternalLinkIcon} size={20} aria-label=\"Opens in a new tab\" />",
110
+ },
111
+ },
112
+ },
113
+ };
114
+ /**
115
+ * Sizes from small (12 px) to large (48 px). The `size` prop sets both
116
+ * `width` and `height` so the icon is always square.
117
+ */
118
+ export var Sizes = {
119
+ parameters: {
120
+ docs: {
121
+ source: {
122
+ code: "<Icon svg={ChevronIcon} size={12} />\n<Icon svg={ChevronIcon} size={16} />\n<Icon svg={ChevronIcon} size={20} />\n<Icon svg={ChevronIcon} size={24} />\n<Icon svg={ChevronIcon} size={32} />\n<Icon svg={ChevronIcon} size={48} />",
123
+ },
124
+ },
125
+ },
126
+ render: function () { return (_jsx("div", { style: { display: 'flex', gap: '1rem', alignItems: 'center' }, children: [12, 16, 20, 24, 32, 48].map(function (size) { return (_jsx(Icon, { svg: ChevronIcon, size: size }, size)); }) })); },
127
+ };
128
+ /**
129
+ * Icons inherit `currentColor` for their stroke/fill, so they automatically
130
+ * adapt to the surrounding text color.
131
+ */
132
+ export var InheritedColor = {
133
+ parameters: {
134
+ docs: {
135
+ source: {
136
+ code: "<p style={{ color: '#3b82f6' }}>\n <Icon svg={StarIcon} size={16} /> Blue text with a blue icon\n</p>\n<p style={{ color: '#ef4444' }}>\n <Icon svg={StarIcon} size={16} /> Red text with a red icon\n</p>",
137
+ },
138
+ },
139
+ },
140
+ render: function () { return (_jsxs("div", { style: { display: 'flex', flexDirection: 'column', gap: '0.5rem' }, children: [_jsxs("p", { style: { color: '#3b82f6', margin: 0, display: 'flex', alignItems: 'center', gap: '0.375rem', fontFamily: 'sans-serif' }, children: [_jsx(Icon, { svg: StarIcon, size: 16 }), " Blue text with a blue icon"] }), _jsxs("p", { style: { color: '#ef4444', margin: 0, display: 'flex', alignItems: 'center', gap: '0.375rem', fontFamily: 'sans-serif' }, children: [_jsx(Icon, { svg: StarIcon, size: 16 }), " Red text with a red icon"] })] })); },
141
+ };
@@ -1,5 +1,5 @@
1
1
  import React from 'react';
2
- interface LinkProps {
2
+ export interface LinkProps {
3
3
  /** URL to link to. External links (starting with http:// or https://) open in new tab */
4
4
  href: string;
5
5
  /** Link content */
@@ -8,4 +8,3 @@ interface LinkProps {
8
8
  className?: string;
9
9
  }
10
10
  export declare function Link({ href, children, className }: LinkProps): import("react/jsx-runtime").JSX.Element;
11
- export {};
@@ -11,7 +11,7 @@ export interface ListProps {
11
11
  /** Custom bullet icon (React node, e.g., SVG) for unordered lists. Takes precedence over bullet prop */
12
12
  bulletIcon?: ReactNode;
13
13
  }
14
- interface ListItemProps {
14
+ export interface ListItemProps {
15
15
  /** List item content */
16
16
  children: ReactNode;
17
17
  /** Additional CSS classes */
@@ -23,4 +23,3 @@ export declare function ListItem({ children, className, bulletIcon }: ListItemPr
23
23
  export declare const List: FC<ListProps> & {
24
24
  Item: typeof ListItem;
25
25
  };
26
- export {};
@@ -1,5 +1,5 @@
1
1
  import React from "react";
2
- interface TabsProps {
2
+ export interface TabsProps {
3
3
  /** ID of the initially active tab */
4
4
  defaultActiveTab?: string;
5
5
  /** Controlled active tab ID */
@@ -12,14 +12,14 @@ interface TabsProps {
12
12
  className?: string;
13
13
  }
14
14
  export declare function Tabs({ defaultActiveTab, activeTab: controlledActiveTab, onTabChange, children, className, }: TabsProps): import("react/jsx-runtime").JSX.Element;
15
- interface TabListProps {
15
+ export interface TabListProps {
16
16
  /** Tab buttons */
17
17
  children: React.ReactNode;
18
18
  /** Additional CSS classes */
19
19
  className?: string;
20
20
  }
21
21
  export declare function TabList({ children, className }: TabListProps): import("react/jsx-runtime").JSX.Element;
22
- interface TabProps {
22
+ export interface TabProps {
23
23
  /** Unique identifier for this tab */
24
24
  id: string;
25
25
  /** Tab label */
@@ -28,7 +28,7 @@ interface TabProps {
28
28
  className?: string;
29
29
  }
30
30
  export declare function Tab({ id, children, className }: TabProps): import("react/jsx-runtime").JSX.Element;
31
- interface TabPanelProps {
31
+ export interface TabPanelProps {
32
32
  /** ID matching the corresponding Tab */
33
33
  id: string;
34
34
  /** Panel content */
@@ -37,4 +37,3 @@ interface TabPanelProps {
37
37
  className?: string;
38
38
  }
39
39
  export declare function TabPanel({ id, children, className }: TabPanelProps): import("react/jsx-runtime").JSX.Element | null;
40
- export {};
@@ -1,5 +1,5 @@
1
1
  import React from 'react';
2
- interface TypographyProps {
2
+ export interface TypographyProps {
3
3
  /**
4
4
  * Typography style variant
5
5
  * @default 'p'
@@ -11,4 +11,3 @@ interface TypographyProps {
11
11
  className?: string;
12
12
  }
13
13
  export declare const Typography: React.FC<TypographyProps>;
14
- export {};
package/dist/index.d.ts CHANGED
@@ -13,4 +13,6 @@ export * from './components/Link';
13
13
  export * from './components/List';
14
14
  export * from './components/Table';
15
15
  export * from './components/Grid';
16
+ export * from './components/Breadcrumb';
17
+ export * from './components/Icon';
16
18
  export * from './hooks/useKeyPress';
package/dist/index.js CHANGED
@@ -14,5 +14,7 @@ export * from './components/Link';
14
14
  export * from './components/List';
15
15
  export * from './components/Table';
16
16
  export * from './components/Grid';
17
+ export * from './components/Breadcrumb';
18
+ export * from './components/Icon';
17
19
  // Export hooks
18
20
  export * from './hooks/useKeyPress';
package/dist/styles.css CHANGED
@@ -286,6 +286,18 @@
286
286
  --dds-grid-gap-lg: 2.5rem; /* gap-10 */
287
287
  --dds-grid-divider-color: #e5e7eb; /* gray-200 */
288
288
  --dds-grid-sticky-top: 1rem;
289
+
290
+ /* Breadcrumb */
291
+ --dds-breadcrumb-font-size-sm: 0.75rem; /* text-xs */
292
+ --dds-breadcrumb-font-size-md: 0.875rem; /* text-sm */
293
+ --dds-breadcrumb-gap: 0.375rem;
294
+ --dds-breadcrumb-link-color: #6b7280; /* gray-500 */
295
+ --dds-breadcrumb-link-color-hover: #111827; /* gray-900 */
296
+ --dds-breadcrumb-current-color: #111827; /* gray-900 */
297
+ --dds-breadcrumb-current-weight: 500;
298
+ --dds-breadcrumb-delimiter-color: #9ca3af; /* gray-400 */
299
+ --dds-breadcrumb-ellipsis-radius: 0.25rem; /* rounded */
300
+ --dds-breadcrumb-ellipsis-hover-bg: #f3f4f6; /* gray-100 */
289
301
  }
290
302
  /* ==========================================================================
291
303
  Dark Mode Tokens
@@ -466,6 +478,13 @@
466
478
 
467
479
  /* Grid */
468
480
  --dds-grid-divider-color: #4b5563; /* gray-600 */
481
+
482
+ /* Breadcrumb */
483
+ --dds-breadcrumb-link-color: #9ca3af; /* gray-400 */
484
+ --dds-breadcrumb-link-color-hover: #f9fafb; /* gray-50 */
485
+ --dds-breadcrumb-current-color: #e5e7eb; /* gray-200 */
486
+ --dds-breadcrumb-delimiter-color: #6b7280; /* gray-500 */
487
+ --dds-breadcrumb-ellipsis-hover-bg: rgba(255, 255, 255, 0.08);
469
488
  }
470
489
  /* Auto dark mode — follows OS preference, opt-out with .dds-light */
471
490
  @media (prefers-color-scheme: dark) {
@@ -641,6 +660,13 @@
641
660
 
642
661
  /* Grid */
643
662
  --dds-grid-divider-color: #4b5563; /* gray-600 */
663
+
664
+ /* Breadcrumb */
665
+ --dds-breadcrumb-link-color: #9ca3af; /* gray-400 */
666
+ --dds-breadcrumb-link-color-hover: #f9fafb; /* gray-50 */
667
+ --dds-breadcrumb-current-color: #e5e7eb; /* gray-200 */
668
+ --dds-breadcrumb-delimiter-color: #6b7280; /* gray-500 */
669
+ --dds-breadcrumb-ellipsis-hover-bg: rgba(255, 255, 255, 0.08);
644
670
  }
645
671
  }
646
672
  /* Explicit dark mode via data attribute (overrides OS preference) */
@@ -795,6 +821,13 @@
795
821
  --dds-table-header-hover-text: #f9fafb;
796
822
  --dds-table-header-active-text: #93c5fd;
797
823
  --dds-table-focus-ring: #60a5fa;
824
+
825
+ /* Breadcrumb */
826
+ --dds-breadcrumb-link-color: #9ca3af; /* gray-400 */
827
+ --dds-breadcrumb-link-color-hover: #f9fafb; /* gray-50 */
828
+ --dds-breadcrumb-current-color: #e5e7eb; /* gray-200 */
829
+ --dds-breadcrumb-delimiter-color: #6b7280; /* gray-500 */
830
+ --dds-breadcrumb-ellipsis-hover-bg: rgba(255, 255, 255, 0.08);
798
831
  }
799
832
  /* Import PrismJS theme */
800
833
  /**
@@ -2136,4 +2169,118 @@ a.no-text-decoration {
2136
2169
  position: static;
2137
2170
  }
2138
2171
  }
2172
+ /* ============================================================
2173
+ Breadcrumb component
2174
+ ============================================================ */
2175
+ .dds-breadcrumb {
2176
+ font-size: var(--dds-breadcrumb-font-size-md);
2177
+ line-height: var(--dds-line-height-tight);
2178
+ }
2179
+ /* Size variants */
2180
+ .dds-breadcrumb--sm {
2181
+ font-size: var(--dds-breadcrumb-font-size-sm);
2182
+ }
2183
+ /* The ordered list */
2184
+ .dds-breadcrumb-list {
2185
+ display: flex;
2186
+ flex-wrap: wrap;
2187
+ align-items: center;
2188
+ list-style: none;
2189
+ margin: 0;
2190
+ padding: 0;
2191
+ gap: var(--dds-breadcrumb-gap);
2192
+ }
2193
+ /* Scroll mode: single row with horizontal scroll (mobile only) */
2194
+ @media (max-width: 640px) {
2195
+ .dds-breadcrumb--scroll .dds-breadcrumb-list {
2196
+ flex-wrap: nowrap;
2197
+ overflow-x: auto;
2198
+ -webkit-overflow-scrolling: touch;
2199
+ scrollbar-width: none; /* Firefox */
2200
+ }
2201
+
2202
+ .dds-breadcrumb--scroll .dds-breadcrumb-list::-webkit-scrollbar {
2203
+ display: none;
2204
+ }
2205
+ }
2206
+ /* Individual segment items */
2207
+ .dds-breadcrumb-item {
2208
+ display: flex;
2209
+ align-items: center;
2210
+ gap: var(--dds-breadcrumb-gap);
2211
+ white-space: nowrap;
2212
+ }
2213
+ /* Linked segments */
2214
+ .dds-breadcrumb-link {
2215
+ color: var(--dds-breadcrumb-link-color);
2216
+ text-decoration: none;
2217
+ transition: color 150ms ease;
2218
+ }
2219
+ a.dds-breadcrumb-link:hover {
2220
+ color: var(--dds-breadcrumb-link-color-hover);
2221
+ text-decoration: underline;
2222
+ }
2223
+ /* Current page segment */
2224
+ .dds-breadcrumb-current {
2225
+ color: var(--dds-breadcrumb-current-color);
2226
+ font-weight: var(--dds-breadcrumb-current-weight);
2227
+ }
2228
+ /* Delimiter characters — aria-hidden in markup */
2229
+ .dds-breadcrumb-delimiter {
2230
+ color: var(--dds-breadcrumb-delimiter-color);
2231
+ user-select: none;
2232
+ flex-shrink: 0;
2233
+ }
2234
+ /* Ellipsis expand button */
2235
+ .dds-breadcrumb-ellipsis {
2236
+ display: inline-flex;
2237
+ align-items: center;
2238
+ background: none;
2239
+ border: none;
2240
+ cursor: pointer;
2241
+ color: var(--dds-breadcrumb-link-color);
2242
+ font-size: inherit;
2243
+ padding: 0.1em 0.3em;
2244
+ line-height: 1;
2245
+ border-radius: var(--dds-breadcrumb-ellipsis-radius);
2246
+ transition: color 150ms ease, background-color 150ms ease;
2247
+ }
2248
+ .dds-breadcrumb-ellipsis:hover {
2249
+ color: var(--dds-breadcrumb-link-color-hover);
2250
+ background-color: var(--dds-breadcrumb-ellipsis-hover-bg);
2251
+ }
2252
+ .dds-breadcrumb-ellipsis:focus-visible {
2253
+ outline: 2px solid var(--dds-breadcrumb-link-color-hover);
2254
+ outline-offset: 2px;
2255
+ }
2256
+ /* ============================================================
2257
+ Collapsible behaviour
2258
+ ============================================================ */
2259
+ /* On desktop the ellipsis is always hidden — show all items */
2260
+ .dds-breadcrumb--collapsible .dds-breadcrumb-item--ellipsis {
2261
+ display: none;
2262
+ }
2263
+ /* On narrow viewports: hide middle items and show ellipsis when not expanded */
2264
+ @media (max-width: 640px) {
2265
+ .dds-breadcrumb--collapsible:not([data-expanded="true"])
2266
+ .dds-breadcrumb-item--middle {
2267
+ display: none;
2268
+ }
2269
+
2270
+ .dds-breadcrumb--collapsible:not([data-expanded="true"])
2271
+ .dds-breadcrumb-item--ellipsis {
2272
+ display: flex;
2273
+ }
2274
+ }
2275
+ .dds-icon {
2276
+ display: inline-flex;
2277
+ align-items: center;
2278
+ justify-content: center;
2279
+ flex-shrink: 0;
2280
+ }
2281
+ /* Ensure an SVG injected via a raw string fills the container */
2282
+ .dds-icon svg {
2283
+ width: 100%;
2284
+ height: 100%;
2285
+ }
2139
2286
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@roadlittledawn/docs-design-system-react",
3
- "version": "0.9.1",
3
+ "version": "0.11.0",
4
4
  "license": "MIT",
5
5
  "description": "React components for documentation design system",
6
6
  "repository": {
@@ -11,7 +11,8 @@
11
11
  "main": "dist/index.js",
12
12
  "types": "dist/index.d.ts",
13
13
  "files": [
14
- "dist"
14
+ "dist",
15
+ "USAGE.md"
15
16
  ],
16
17
  "scripts": {
17
18
  "build": "tsc && npm run build:css",