@roadlittledawn/docs-design-system-react 0.12.3 → 0.13.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/USAGE.md +140 -0
- package/dist/components/Card.d.ts +12 -1
- package/dist/components/Card.js +4 -3
- package/dist/components/Card.stories.d.ts +5 -0
- package/dist/components/Card.stories.js +28 -0
- package/dist/components/Tile.d.ts +24 -0
- package/dist/components/Tile.js +19 -0
- package/dist/components/Tile.stories.d.ts +40 -0
- package/dist/components/Tile.stories.js +180 -0
- package/dist/components/TileGrid.d.ts +15 -0
- package/dist/components/TileGrid.js +13 -0
- package/dist/components/TileGrid.stories.d.ts +13 -0
- package/dist/components/TileGrid.stories.js +96 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +2 -0
- package/dist/styles.css +218 -0
- package/package.json +1 -1
package/USAGE.md
CHANGED
|
@@ -36,6 +36,7 @@ Before diving into individual components, here's guidance on common decision poi
|
|
|
36
36
|
|
|
37
37
|
- **`Grid`/`Column`** — for asymmetric or varied multi-column layouts (tutorial + code panel, image + annotation, side-by-side comparison)
|
|
38
38
|
- **`CardGrid`** — specifically for grids of uniform `Card` items; simpler API than `Grid`
|
|
39
|
+
- **`TileGrid`** — for dense grids of `Tile` items (integrations, frameworks, skills); supports 3–6 columns
|
|
39
40
|
|
|
40
41
|
### Headings and text
|
|
41
42
|
|
|
@@ -114,6 +115,10 @@ A contained, elevated box for grouping related content. Becomes a fully-clickabl
|
|
|
114
115
|
- **Alerts, warnings, or notices** — use `Callout`
|
|
115
116
|
- **Very long content** — cards are best for summaries; long content belongs in page sections
|
|
116
117
|
|
|
118
|
+
### When to Use `Card` vs `Tile`
|
|
119
|
+
|
|
120
|
+
Use `Card` for a small number of items (2–12) with substantial content (descriptions, icons, links). Use `Tile` for large lists of compact items (integrations, frameworks, skills) where 10–100+ items fit on a page.
|
|
121
|
+
|
|
117
122
|
### When to Use `Card` vs `Callout`
|
|
118
123
|
|
|
119
124
|
Use `Card` for content that is part of the normal page structure (navigation, feature highlights). Use `Callout` when you need to interrupt the reading flow to highlight something important, warn users, or provide tips.
|
|
@@ -136,6 +141,8 @@ import { Card } from "@roadlittledawn/docs-design-system-react";
|
|
|
136
141
|
| `iconPlacement` | `"left" \| "top-left" \| "top-center"` | `"top-left"` | Where to place the icon: vertically centered on the left, above content flush left, or above content centered |
|
|
137
142
|
| `iconSize` | `string` | — | Override the icon container size (width and height). Accepts any valid CSS length (e.g. `"2rem"`, `"48px"`). Defaults to `--dds-card-icon-size` (`1.5rem`). |
|
|
138
143
|
| `showArrow` | `boolean` | `false` | Show an animated arrow in the lower-right corner to signal the card is navigable. Best used with `href`. |
|
|
144
|
+
| `maxWidth` | `string` | — | Constrain the card's maximum width (e.g. `"400px"`, `"32rem"`). Useful when a card fills a wide column but looks better smaller. |
|
|
145
|
+
| `centered` | `boolean` | `false` | Horizontally center the card within its container. Most useful combined with `maxWidth`. |
|
|
139
146
|
| `children` | `ReactNode` | — | Card content |
|
|
140
147
|
| `className` | `string` | `""` | Additional CSS classes |
|
|
141
148
|
|
|
@@ -171,6 +178,11 @@ import { Card } from "@roadlittledawn/docs-design-system-react";
|
|
|
171
178
|
<Card title="New Feature" titleColor="blue" backgroundColor="blue">
|
|
172
179
|
Check out our latest component additions.
|
|
173
180
|
</Card>
|
|
181
|
+
|
|
182
|
+
{/* Custom width, centered in column */}
|
|
183
|
+
<Card title="Centered Card" maxWidth="400px" centered>
|
|
184
|
+
This card is constrained to 400px and centered.
|
|
185
|
+
</Card>
|
|
174
186
|
```
|
|
175
187
|
|
|
176
188
|
### Icon usage in MDX
|
|
@@ -258,6 +270,134 @@ import { CardGrid } from "@roadlittledawn/docs-design-system-react";
|
|
|
258
270
|
|
|
259
271
|
---
|
|
260
272
|
|
|
273
|
+
## Tile
|
|
274
|
+
|
|
275
|
+
A compact, clickable item designed for dense listing patterns — integrations, frameworks, plugins, skills, etc. Unlike `Card`, Tile has a fixed layout (icon left, title right) and a simpler, more opinionated API.
|
|
276
|
+
|
|
277
|
+
### When to Use
|
|
278
|
+
|
|
279
|
+
- Large lists of similar items (10–100+): integrations, frameworks, plugins, skills
|
|
280
|
+
- Navigation to many destinations where individual items are brief
|
|
281
|
+
- When `Card` would feel too spacious for the number of items
|
|
282
|
+
|
|
283
|
+
### When Not to Use
|
|
284
|
+
|
|
285
|
+
- **Fewer than ~6 items with significant content** — use `Card` instead
|
|
286
|
+
- **Items that need rich content** (paragraphs, code, images) — use `Card`
|
|
287
|
+
- **Feature highlights** — use `Card` with colored backgrounds
|
|
288
|
+
|
|
289
|
+
### `Tile` vs `Card`
|
|
290
|
+
|
|
291
|
+
| | Tile | Card |
|
|
292
|
+
|---|---|---|
|
|
293
|
+
| Layout | Fixed (icon-left) | Flexible (icon top/left/center) |
|
|
294
|
+
| Title | Required | Optional |
|
|
295
|
+
| Description | String prop | `children` (ReactNode) |
|
|
296
|
+
| Use case | Dense lists (10–100+ items) | Content groups (2–12 items) |
|
|
297
|
+
| Padding | Compact (0.875rem) | Spacious (1.5rem) |
|
|
298
|
+
|
|
299
|
+
### Import
|
|
300
|
+
|
|
301
|
+
```tsx
|
|
302
|
+
import { Tile } from "@roadlittledawn/docs-design-system-react";
|
|
303
|
+
```
|
|
304
|
+
|
|
305
|
+
### Props
|
|
306
|
+
|
|
307
|
+
| Prop | Type | Default | Description |
|
|
308
|
+
| --- | --- | --- | --- |
|
|
309
|
+
| `title` | `string` | — | **Required.** Tile heading text |
|
|
310
|
+
| `icon` | `ReactNode` | — | Optional icon displayed on the left. Pass a rendered icon component. |
|
|
311
|
+
| `description` | `string` | — | Optional short description below the title |
|
|
312
|
+
| `href` | `string` | — | Optional link URL. Makes the entire tile clickable. |
|
|
313
|
+
| `showArrow` | `boolean` | `false` | Show an animated arrow in the lower-right corner. Best used with `href`. |
|
|
314
|
+
| `className` | `string` | `""` | Additional CSS classes |
|
|
315
|
+
|
|
316
|
+
### Examples
|
|
317
|
+
|
|
318
|
+
```tsx
|
|
319
|
+
{/* Basic tile */}
|
|
320
|
+
<Tile title="React" icon={<ReactIcon />} href="/integrations/react" />
|
|
321
|
+
|
|
322
|
+
{/* With description */}
|
|
323
|
+
<Tile title="React" icon={<ReactIcon />} description="Build UIs with components" href="/integrations/react" />
|
|
324
|
+
|
|
325
|
+
{/* With arrow indicator */}
|
|
326
|
+
<Tile title="TypeScript" icon={<TsIcon />} description="Typed JavaScript" href="/skills/typescript" showArrow />
|
|
327
|
+
```
|
|
328
|
+
|
|
329
|
+
### Icon usage in MDX
|
|
330
|
+
|
|
331
|
+
Same pattern as `Card` — the consuming site resolves icon name strings to rendered components in the MDX component map:
|
|
332
|
+
|
|
333
|
+
```tsx
|
|
334
|
+
Tile: ({ icon, ...props }) => {
|
|
335
|
+
const IconComp = typeof icon === 'string' ? iconMap[icon] : null;
|
|
336
|
+
return <DdsTile icon={IconComp ? <IconComp /> : icon} {...props} />;
|
|
337
|
+
}
|
|
338
|
+
```
|
|
339
|
+
|
|
340
|
+
---
|
|
341
|
+
|
|
342
|
+
## TileGrid
|
|
343
|
+
|
|
344
|
+
A responsive CSS grid container designed for `Tile` components. Supports 3–6 columns and automatically adjusts to fewer columns on smaller screens.
|
|
345
|
+
|
|
346
|
+
### When to Use
|
|
347
|
+
|
|
348
|
+
- Laying out 6 or more `Tile` components in a grid
|
|
349
|
+
- Displaying integrations, plugins, or skills lists
|
|
350
|
+
|
|
351
|
+
### When Not to Use
|
|
352
|
+
|
|
353
|
+
- **Mixed content types** — use `Grid`
|
|
354
|
+
- **Fewer than 6 tiles** — `CardGrid` may be more appropriate
|
|
355
|
+
- **Tiles that need equal height** — TileGrid tiles size to their content; for equal-height use `CardGrid` with Card
|
|
356
|
+
|
|
357
|
+
### Responsive behavior
|
|
358
|
+
|
|
359
|
+
| columns | Mobile (< 640px) | Tablet (≥ 640px) | Desktop (≥ 1024px) |
|
|
360
|
+
|---------|-----------------|-----------------|-------------------|
|
|
361
|
+
| 3 | 1 col | 2 col | 3 col |
|
|
362
|
+
| 4 | 2 col | 2 col | 4 col |
|
|
363
|
+
| 5 | 2 col | 3 col | 5 col |
|
|
364
|
+
| 6 | 2 col | 3 col | 6 col |
|
|
365
|
+
|
|
366
|
+
### Import
|
|
367
|
+
|
|
368
|
+
```tsx
|
|
369
|
+
import { TileGrid } from "@roadlittledawn/docs-design-system-react";
|
|
370
|
+
```
|
|
371
|
+
|
|
372
|
+
### Props
|
|
373
|
+
|
|
374
|
+
| Prop | Type | Default | Description |
|
|
375
|
+
| --- | --- | --- | --- |
|
|
376
|
+
| `columns` | `3 \| 4 \| 5 \| 6` | `4` | Number of columns at full width |
|
|
377
|
+
| `children` | `ReactNode` | — | Grid content (typically Tile components) |
|
|
378
|
+
| `className` | `string` | `""` | Additional CSS classes |
|
|
379
|
+
|
|
380
|
+
### Examples
|
|
381
|
+
|
|
382
|
+
```tsx
|
|
383
|
+
{/* Integration list */}
|
|
384
|
+
<TileGrid columns={4}>
|
|
385
|
+
<Tile title="React" icon={<ReactIcon />} href="/integrations/react" />
|
|
386
|
+
<Tile title="Vue" icon={<VueIcon />} href="/integrations/vue" />
|
|
387
|
+
<Tile title="Angular" icon={<AngularIcon />} href="/integrations/angular" />
|
|
388
|
+
{/* …more tiles */}
|
|
389
|
+
</TileGrid>
|
|
390
|
+
|
|
391
|
+
{/* Skills with descriptions */}
|
|
392
|
+
<TileGrid columns={3}>
|
|
393
|
+
<Tile title="TypeScript" icon={<TsIcon />} description="Typed JavaScript" href="/skills/typescript" showArrow />
|
|
394
|
+
<Tile title="GraphQL" icon={<GqlIcon />} description="Query language for APIs" href="/skills/graphql" showArrow />
|
|
395
|
+
{/* …more tiles */}
|
|
396
|
+
</TileGrid>
|
|
397
|
+
```
|
|
398
|
+
|
|
399
|
+
---
|
|
400
|
+
|
|
261
401
|
## Callout
|
|
262
402
|
|
|
263
403
|
Interrupts reading flow to highlight important information. Each variant has a specific semantic meaning — choose the right one.
|
|
@@ -39,9 +39,20 @@ export interface CardProps {
|
|
|
39
39
|
* @default false
|
|
40
40
|
*/
|
|
41
41
|
showArrow?: boolean;
|
|
42
|
+
/**
|
|
43
|
+
* Constrain the card's maximum width. Accepts any valid CSS length value (e.g. `"400px"`, `"32rem"`).
|
|
44
|
+
* Useful when a card fills a wide column but its content looks better at a smaller size.
|
|
45
|
+
*/
|
|
46
|
+
maxWidth?: string;
|
|
47
|
+
/**
|
|
48
|
+
* When true, horizontally centers the card within its container.
|
|
49
|
+
* Most useful in combination with `maxWidth`.
|
|
50
|
+
* @default false
|
|
51
|
+
*/
|
|
52
|
+
centered?: boolean;
|
|
42
53
|
/** Card content */
|
|
43
54
|
children: ReactNode;
|
|
44
55
|
/** Additional CSS classes */
|
|
45
56
|
className?: string;
|
|
46
57
|
}
|
|
47
|
-
export declare function Card({ title, titleColor, backgroundColor, href, icon, iconPlacement, iconSize, showArrow, children, className, }: CardProps): import("react/jsx-runtime").JSX.Element;
|
|
58
|
+
export declare function Card({ title, titleColor, backgroundColor, href, icon, iconPlacement, iconSize, showArrow, maxWidth, centered, children, className, }: CardProps): import("react/jsx-runtime").JSX.Element;
|
package/dist/components/Card.js
CHANGED
|
@@ -11,7 +11,7 @@ var __assign = (this && this.__assign) || function () {
|
|
|
11
11
|
};
|
|
12
12
|
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
13
13
|
export function Card(_a) {
|
|
14
|
-
var title = _a.title, _b = _a.titleColor, titleColor = _b === void 0 ? "gray" : _b, _c = _a.backgroundColor, backgroundColor = _c === void 0 ? "white" : _c, href = _a.href, icon = _a.icon, _d = _a.iconPlacement, iconPlacement = _d === void 0 ? "top-left" : _d, iconSize = _a.iconSize, _e = _a.showArrow, showArrow = _e === void 0 ? false : _e, children = _a.children,
|
|
14
|
+
var title = _a.title, _b = _a.titleColor, titleColor = _b === void 0 ? "gray" : _b, _c = _a.backgroundColor, backgroundColor = _c === void 0 ? "white" : _c, href = _a.href, icon = _a.icon, _d = _a.iconPlacement, iconPlacement = _d === void 0 ? "top-left" : _d, iconSize = _a.iconSize, _e = _a.showArrow, showArrow = _e === void 0 ? false : _e, maxWidth = _a.maxWidth, _f = _a.centered, centered = _f === void 0 ? false : _f, children = _a.children, _g = _a.className, className = _g === void 0 ? "" : _g;
|
|
15
15
|
var cardClasses = [
|
|
16
16
|
"dds-card",
|
|
17
17
|
"dds-card-bg-".concat(backgroundColor),
|
|
@@ -33,9 +33,10 @@ export function Card(_a) {
|
|
|
33
33
|
.join(" "), style: __assign({}, (iconSize && { width: iconSize, height: iconSize })), "aria-hidden": "true", children: icon })) : null;
|
|
34
34
|
var arrowEl = showArrow ? (_jsx("span", { className: "dds-card-arrow", "aria-hidden": "true", children: _jsx("svg", { width: "16", height: "16", viewBox: "0 0 16 16", fill: "none", xmlns: "http://www.w3.org/2000/svg", children: _jsx("path", { d: "M2 8H14M10 4L14 8L10 12", stroke: "currentColor", strokeWidth: "1.5", strokeLinecap: "round", strokeLinejoin: "round" }) }) })) : null;
|
|
35
35
|
var bodyContent = icon && iconPlacement === "left" ? (_jsxs("div", { className: "dds-card-icon-row", children: [iconEl, _jsxs("div", { className: "dds-card-icon-content", children: [title && _jsx("h3", { className: titleClasses, children: title }), _jsx("div", { className: textClasses, children: children })] })] })) : (_jsxs(_Fragment, { children: [iconEl, title && _jsx("h3", { className: titleClasses, children: title }), _jsx("div", { className: textClasses, children: children })] }));
|
|
36
|
-
var
|
|
36
|
+
var outerStyle = __assign(__assign({}, (maxWidth && { maxWidth: maxWidth })), (centered && { marginLeft: "auto", marginRight: "auto" }));
|
|
37
|
+
var content = (_jsxs("div", { className: cardClasses, style: !href ? outerStyle : undefined, children: [bodyContent, arrowEl] }));
|
|
37
38
|
if (href) {
|
|
38
|
-
return (_jsx("a", { href: href, className: "no-text-decoration", children: content }));
|
|
39
|
+
return (_jsx("a", { href: href, className: "no-text-decoration", style: Object.keys(outerStyle).length > 0 ? outerStyle : undefined, children: content }));
|
|
39
40
|
}
|
|
40
41
|
return content;
|
|
41
42
|
}
|
|
@@ -55,6 +55,11 @@ export declare const WithCustomIconSize: Story;
|
|
|
55
55
|
* All title color variants.
|
|
56
56
|
*/
|
|
57
57
|
export declare const TitleColors: Story;
|
|
58
|
+
/**
|
|
59
|
+
* Card constrained to a custom width and centered. Useful when a card fills a wide column but
|
|
60
|
+
* looks better at a smaller size.
|
|
61
|
+
*/
|
|
62
|
+
export declare const CustomWidth: Story;
|
|
58
63
|
/**
|
|
59
64
|
* All background color variants.
|
|
60
65
|
*/
|
|
@@ -51,6 +51,15 @@ var meta = {
|
|
|
51
51
|
control: 'text',
|
|
52
52
|
description: 'Card content.',
|
|
53
53
|
},
|
|
54
|
+
maxWidth: {
|
|
55
|
+
control: 'text',
|
|
56
|
+
description: 'Constrain the card\'s maximum width (e.g. "400px", "32rem").',
|
|
57
|
+
},
|
|
58
|
+
centered: {
|
|
59
|
+
control: 'boolean',
|
|
60
|
+
description: 'Horizontally center the card within its container. Most useful with maxWidth.',
|
|
61
|
+
table: { defaultValue: { summary: 'false' } },
|
|
62
|
+
},
|
|
54
63
|
className: {
|
|
55
64
|
control: 'text',
|
|
56
65
|
description: 'Additional CSS classes.',
|
|
@@ -266,6 +275,25 @@ export var TitleColors = {
|
|
|
266
275
|
},
|
|
267
276
|
render: function () { return (_jsxs("div", { style: { display: 'flex', flexDirection: 'column', gap: '1rem' }, children: [_jsx(Card, { title: "Blue Title", titleColor: "blue", children: "Content with blue title" }), _jsx(Card, { title: "Green Title", titleColor: "green", children: "Content with green title" }), _jsx(Card, { title: "Purple Title", titleColor: "purple", children: "Content with purple title" }), _jsx(Card, { title: "Red Title", titleColor: "red", children: "Content with red title" }), _jsx(Card, { title: "Yellow Title", titleColor: "yellow", children: "Content with yellow title" }), _jsx(Card, { title: "Gray Title", titleColor: "gray", children: "Content with gray title" })] })); },
|
|
268
277
|
};
|
|
278
|
+
/**
|
|
279
|
+
* Card constrained to a custom width and centered. Useful when a card fills a wide column but
|
|
280
|
+
* looks better at a smaller size.
|
|
281
|
+
*/
|
|
282
|
+
export var CustomWidth = {
|
|
283
|
+
args: {
|
|
284
|
+
title: 'Custom Width',
|
|
285
|
+
maxWidth: '400px',
|
|
286
|
+
centered: true,
|
|
287
|
+
children: 'This card is constrained to 400px and centered in its container.',
|
|
288
|
+
},
|
|
289
|
+
parameters: {
|
|
290
|
+
docs: {
|
|
291
|
+
source: {
|
|
292
|
+
code: "<Card title=\"Custom Width\" maxWidth=\"400px\" centered>\n This card is constrained to 400px and centered in its container.\n</Card>",
|
|
293
|
+
},
|
|
294
|
+
},
|
|
295
|
+
},
|
|
296
|
+
};
|
|
269
297
|
/**
|
|
270
298
|
* All background color variants.
|
|
271
299
|
*/
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { ReactNode } from "react";
|
|
2
|
+
import "./Tile.css";
|
|
3
|
+
export interface TileProps {
|
|
4
|
+
/** Tile heading text (required) */
|
|
5
|
+
title: string;
|
|
6
|
+
/**
|
|
7
|
+
* Optional icon to display on the left side.
|
|
8
|
+
* Pass a rendered icon component (e.g. `<YourIcon />`).
|
|
9
|
+
*/
|
|
10
|
+
icon?: ReactNode;
|
|
11
|
+
/** Optional short description displayed below the title */
|
|
12
|
+
description?: string;
|
|
13
|
+
/** Optional link URL. When provided, the entire tile becomes clickable. */
|
|
14
|
+
href?: string;
|
|
15
|
+
/**
|
|
16
|
+
* Show an animated arrow in the lower-right corner to signal the tile is navigable.
|
|
17
|
+
* Best used together with `href`.
|
|
18
|
+
* @default false
|
|
19
|
+
*/
|
|
20
|
+
showArrow?: boolean;
|
|
21
|
+
/** Additional CSS classes */
|
|
22
|
+
className?: string;
|
|
23
|
+
}
|
|
24
|
+
export declare function Tile({ title, icon, description, href, showArrow, className, }: TileProps): import("react/jsx-runtime").JSX.Element;
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import "./Tile.css";
|
|
3
|
+
export function Tile(_a) {
|
|
4
|
+
var title = _a.title, icon = _a.icon, description = _a.description, href = _a.href, _b = _a.showArrow, showArrow = _b === void 0 ? false : _b, _c = _a.className, className = _c === void 0 ? "" : _c;
|
|
5
|
+
var tileClasses = [
|
|
6
|
+
"dds-tile",
|
|
7
|
+
href ? "dds-tile-clickable" : "",
|
|
8
|
+
showArrow ? "dds-tile-has-arrow" : "",
|
|
9
|
+
className,
|
|
10
|
+
]
|
|
11
|
+
.filter(Boolean)
|
|
12
|
+
.join(" ");
|
|
13
|
+
var arrowEl = showArrow ? (_jsx("span", { className: "dds-tile-arrow", "aria-hidden": "true", children: _jsx("svg", { width: "14", height: "14", viewBox: "0 0 16 16", fill: "none", xmlns: "http://www.w3.org/2000/svg", children: _jsx("path", { d: "M2 8H14M10 4L14 8L10 12", stroke: "currentColor", strokeWidth: "1.5", strokeLinecap: "round", strokeLinejoin: "round" }) }) })) : null;
|
|
14
|
+
var content = (_jsxs("div", { className: tileClasses, children: [_jsxs("div", { className: "dds-tile-body", children: [icon && (_jsx("span", { className: "dds-tile-icon", "aria-hidden": "true", children: icon })), _jsxs("div", { className: "dds-tile-text", children: [_jsx("span", { className: "dds-tile-title", children: title }), description && (_jsx("span", { className: "dds-tile-desc", children: description }))] })] }), arrowEl] }));
|
|
15
|
+
if (href) {
|
|
16
|
+
return (_jsx("a", { href: href, className: "dds-tile-link", children: content }));
|
|
17
|
+
}
|
|
18
|
+
return content;
|
|
19
|
+
}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import type { Meta, StoryObj } from '@storybook/react';
|
|
2
|
+
import { Tile } from './Tile';
|
|
3
|
+
/**
|
|
4
|
+
* The Tile component is a compact, clickable card designed for dense lists of items
|
|
5
|
+
* such as integrations, frameworks, plugins, or skills.
|
|
6
|
+
*
|
|
7
|
+
* Unlike Card, Tile has a fixed layout (icon left, title right) and a simpler API
|
|
8
|
+
* focused on the list-item use case.
|
|
9
|
+
*/
|
|
10
|
+
declare const meta: Meta<typeof Tile>;
|
|
11
|
+
export default meta;
|
|
12
|
+
type Story = StoryObj<typeof Tile>;
|
|
13
|
+
/**
|
|
14
|
+
* Basic tile with title only.
|
|
15
|
+
*/
|
|
16
|
+
export declare const Basic: Story;
|
|
17
|
+
/**
|
|
18
|
+
* Tile with an icon on the left.
|
|
19
|
+
*/
|
|
20
|
+
export declare const WithIcon: Story;
|
|
21
|
+
/**
|
|
22
|
+
* Tile with icon and a short description below the title.
|
|
23
|
+
*/
|
|
24
|
+
export declare const WithDescription: Story;
|
|
25
|
+
/**
|
|
26
|
+
* Clickable tile with a link. Hover to see the border and shadow change.
|
|
27
|
+
*/
|
|
28
|
+
export declare const Clickable: Story;
|
|
29
|
+
/**
|
|
30
|
+
* Clickable tile with the animated arrow indicator. Hover to see it animate.
|
|
31
|
+
*/
|
|
32
|
+
export declare const WithArrow: Story;
|
|
33
|
+
/**
|
|
34
|
+
* A realistic grid of integration tiles. Use TileGrid to lay out multiple Tiles.
|
|
35
|
+
*/
|
|
36
|
+
export declare const IntegrationGrid: Story;
|
|
37
|
+
/**
|
|
38
|
+
* A larger grid with descriptions and arrows, using 3 columns.
|
|
39
|
+
*/
|
|
40
|
+
export declare const SkillsGrid: Story;
|
|
@@ -0,0 +1,180 @@
|
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
import { Tile } from './Tile';
|
|
3
|
+
import { TileGrid } from './TileGrid';
|
|
4
|
+
/**
|
|
5
|
+
* The Tile component is a compact, clickable card designed for dense lists of items
|
|
6
|
+
* such as integrations, frameworks, plugins, or skills.
|
|
7
|
+
*
|
|
8
|
+
* Unlike Card, Tile has a fixed layout (icon left, title right) and a simpler API
|
|
9
|
+
* focused on the list-item use case.
|
|
10
|
+
*/
|
|
11
|
+
var meta = {
|
|
12
|
+
title: 'Components/Tile',
|
|
13
|
+
component: Tile,
|
|
14
|
+
tags: ['autodocs'],
|
|
15
|
+
argTypes: {
|
|
16
|
+
title: {
|
|
17
|
+
control: 'text',
|
|
18
|
+
description: 'Tile heading text (required).',
|
|
19
|
+
},
|
|
20
|
+
icon: {
|
|
21
|
+
control: false,
|
|
22
|
+
description: 'Optional icon to display on the left. Pass a rendered icon component.',
|
|
23
|
+
},
|
|
24
|
+
description: {
|
|
25
|
+
control: 'text',
|
|
26
|
+
description: 'Optional short description displayed below the title.',
|
|
27
|
+
},
|
|
28
|
+
href: {
|
|
29
|
+
control: 'text',
|
|
30
|
+
description: 'Optional link URL. Makes the entire tile clickable.',
|
|
31
|
+
},
|
|
32
|
+
showArrow: {
|
|
33
|
+
control: 'boolean',
|
|
34
|
+
description: 'Show an animated arrow in the lower-right corner.',
|
|
35
|
+
table: { defaultValue: { summary: 'false' } },
|
|
36
|
+
},
|
|
37
|
+
className: {
|
|
38
|
+
control: 'text',
|
|
39
|
+
description: 'Additional CSS classes.',
|
|
40
|
+
},
|
|
41
|
+
},
|
|
42
|
+
parameters: {
|
|
43
|
+
docs: {
|
|
44
|
+
description: {
|
|
45
|
+
component: "\nThe Tile component is a compact, clickable item designed for dense listing patterns.\n\n## When to Use\n\n- Long lists of integrations, frameworks, plugins, or skills\n- Navigation to many similar destinations (e.g., 20+ items)\n- When Card would feel too spacious for the number of items\n\n## When Not to Use\n\n- For fewer than ~6 items with significant content \u2014 use Card instead\n- When items need rich content (paragraphs, code, images) \u2014 use Card\n- For feature highlights \u2014 use Card with colored backgrounds\n\n## Vs. Card\n\n| | Tile | Card |\n|---|---|---|\n| Layout | Fixed (icon-left) | Flexible (icon top/left/center) |\n| Title | Required | Optional |\n| Description | String prop | children (ReactNode) |\n| Use case | Dense lists (10\u2013100+ items) | Content groups (2\u201312 items) |\n| Padding | Compact (0.875rem) | Spacious (1.5rem) |\n\n## Accessibility\n\n- Clickable tiles use proper link semantics (`<a>`)\n- Icon is decorative (`aria-hidden`) and does not affect screen reader output\n ",
|
|
46
|
+
},
|
|
47
|
+
},
|
|
48
|
+
},
|
|
49
|
+
};
|
|
50
|
+
export default meta;
|
|
51
|
+
var DemoIcon = function () { return (_jsx("svg", { viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "1.5", strokeLinecap: "round", strokeLinejoin: "round", children: _jsx("path", { d: "M12 6.042A8.967 8.967 0 0 0 6 3.75c-1.052 0-2.062.18-3 .512v14.25A8.987 8.987 0 0 1 6 18c2.305 0 4.408.867 6 2.292m0-14.25a8.966 8.966 0 0 1 6-2.292c1.052 0 2.062.18 3 .512v14.25A8.987 8.987 0 0 0 18 18a8.967 8.967 0 0 0-6 2.292m0-14.25v14.25" }) })); };
|
|
52
|
+
/**
|
|
53
|
+
* Basic tile with title only.
|
|
54
|
+
*/
|
|
55
|
+
export var Basic = {
|
|
56
|
+
args: {
|
|
57
|
+
title: 'React',
|
|
58
|
+
},
|
|
59
|
+
parameters: {
|
|
60
|
+
docs: {
|
|
61
|
+
source: { code: "<Tile title=\"React\" />" },
|
|
62
|
+
},
|
|
63
|
+
},
|
|
64
|
+
};
|
|
65
|
+
/**
|
|
66
|
+
* Tile with an icon on the left.
|
|
67
|
+
*/
|
|
68
|
+
export var WithIcon = {
|
|
69
|
+
args: {
|
|
70
|
+
title: 'React',
|
|
71
|
+
icon: _jsx(DemoIcon, {}),
|
|
72
|
+
},
|
|
73
|
+
parameters: {
|
|
74
|
+
docs: {
|
|
75
|
+
source: {
|
|
76
|
+
code: "<Tile title=\"React\" icon={<ReactIcon />} />",
|
|
77
|
+
},
|
|
78
|
+
},
|
|
79
|
+
},
|
|
80
|
+
};
|
|
81
|
+
/**
|
|
82
|
+
* Tile with icon and a short description below the title.
|
|
83
|
+
*/
|
|
84
|
+
export var WithDescription = {
|
|
85
|
+
args: {
|
|
86
|
+
title: 'React',
|
|
87
|
+
icon: _jsx(DemoIcon, {}),
|
|
88
|
+
description: 'Build UIs with components',
|
|
89
|
+
},
|
|
90
|
+
parameters: {
|
|
91
|
+
docs: {
|
|
92
|
+
source: {
|
|
93
|
+
code: "<Tile title=\"React\" icon={<ReactIcon />} description=\"Build UIs with components\" />",
|
|
94
|
+
},
|
|
95
|
+
},
|
|
96
|
+
},
|
|
97
|
+
};
|
|
98
|
+
/**
|
|
99
|
+
* Clickable tile with a link. Hover to see the border and shadow change.
|
|
100
|
+
*/
|
|
101
|
+
export var Clickable = {
|
|
102
|
+
args: {
|
|
103
|
+
title: 'React',
|
|
104
|
+
icon: _jsx(DemoIcon, {}),
|
|
105
|
+
description: 'Build UIs with components',
|
|
106
|
+
href: '/integrations/react',
|
|
107
|
+
},
|
|
108
|
+
parameters: {
|
|
109
|
+
docs: {
|
|
110
|
+
source: {
|
|
111
|
+
code: "<Tile title=\"React\" icon={<ReactIcon />} description=\"Build UIs with components\" href=\"/integrations/react\" />",
|
|
112
|
+
},
|
|
113
|
+
},
|
|
114
|
+
},
|
|
115
|
+
};
|
|
116
|
+
/**
|
|
117
|
+
* Clickable tile with the animated arrow indicator. Hover to see it animate.
|
|
118
|
+
*/
|
|
119
|
+
export var WithArrow = {
|
|
120
|
+
args: {
|
|
121
|
+
title: 'React',
|
|
122
|
+
icon: _jsx(DemoIcon, {}),
|
|
123
|
+
description: 'Build UIs with components',
|
|
124
|
+
href: '/integrations/react',
|
|
125
|
+
showArrow: true,
|
|
126
|
+
},
|
|
127
|
+
parameters: {
|
|
128
|
+
docs: {
|
|
129
|
+
source: {
|
|
130
|
+
code: "<Tile title=\"React\" icon={<ReactIcon />} description=\"Build UIs with components\" href=\"/integrations/react\" showArrow />",
|
|
131
|
+
},
|
|
132
|
+
},
|
|
133
|
+
},
|
|
134
|
+
};
|
|
135
|
+
/**
|
|
136
|
+
* A realistic grid of integration tiles. Use TileGrid to lay out multiple Tiles.
|
|
137
|
+
*/
|
|
138
|
+
export var IntegrationGrid = {
|
|
139
|
+
parameters: {
|
|
140
|
+
docs: {
|
|
141
|
+
source: {
|
|
142
|
+
code: "<TileGrid columns={4}>\n <Tile title=\"React\" icon={<ReactIcon />} description=\"UI components\" href=\"/integrations/react\" />\n <Tile title=\"Vue\" icon={<VueIcon />} description=\"Progressive framework\" href=\"/integrations/vue\" />\n {/* \u2026more tiles */}\n</TileGrid>",
|
|
143
|
+
},
|
|
144
|
+
},
|
|
145
|
+
},
|
|
146
|
+
render: function () {
|
|
147
|
+
var integrations = [
|
|
148
|
+
'React', 'Vue', 'Angular', 'Svelte',
|
|
149
|
+
'Next.js', 'Nuxt', 'Astro', 'Remix',
|
|
150
|
+
'Node.js', 'Deno', 'Bun', 'Express',
|
|
151
|
+
];
|
|
152
|
+
return (_jsx(TileGrid, { columns: 4, children: integrations.map(function (name) { return (_jsx(Tile, { title: name, icon: _jsx(DemoIcon, {}), href: "/integrations/".concat(name.toLowerCase()) }, name)); }) }));
|
|
153
|
+
},
|
|
154
|
+
};
|
|
155
|
+
/**
|
|
156
|
+
* A larger grid with descriptions and arrows, using 3 columns.
|
|
157
|
+
*/
|
|
158
|
+
export var SkillsGrid = {
|
|
159
|
+
parameters: {
|
|
160
|
+
docs: {
|
|
161
|
+
source: {
|
|
162
|
+
code: "<TileGrid columns={3}>\n <Tile title=\"TypeScript\" icon={<TsIcon />} description=\"Typed JavaScript\" href=\"/skills/typescript\" showArrow />\n {/* \u2026more tiles */}\n</TileGrid>",
|
|
163
|
+
},
|
|
164
|
+
},
|
|
165
|
+
},
|
|
166
|
+
render: function () {
|
|
167
|
+
var skills = [
|
|
168
|
+
{ name: 'TypeScript', desc: 'Typed JavaScript at scale' },
|
|
169
|
+
{ name: 'GraphQL', desc: 'Query language for APIs' },
|
|
170
|
+
{ name: 'Docker', desc: 'Container platform' },
|
|
171
|
+
{ name: 'Kubernetes', desc: 'Container orchestration' },
|
|
172
|
+
{ name: 'Terraform', desc: 'Infrastructure as code' },
|
|
173
|
+
{ name: 'Postgres', desc: 'Relational database' },
|
|
174
|
+
];
|
|
175
|
+
return (_jsx(TileGrid, { columns: 3, children: skills.map(function (_a) {
|
|
176
|
+
var name = _a.name, desc = _a.desc;
|
|
177
|
+
return (_jsx(Tile, { title: name, icon: _jsx(DemoIcon, {}), description: desc, href: "/skills/".concat(name.toLowerCase()), showArrow: true }, name));
|
|
178
|
+
}) }));
|
|
179
|
+
},
|
|
180
|
+
};
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { ReactNode } from "react";
|
|
2
|
+
import "./TileGrid.css";
|
|
3
|
+
export interface TileGridProps {
|
|
4
|
+
/**
|
|
5
|
+
* Number of columns in the grid at full width.
|
|
6
|
+
* Responsive breakpoints apply automatically.
|
|
7
|
+
* @default 4
|
|
8
|
+
*/
|
|
9
|
+
columns?: 3 | 4 | 5 | 6;
|
|
10
|
+
/** Grid content (typically Tile components) */
|
|
11
|
+
children: ReactNode;
|
|
12
|
+
/** Additional CSS classes */
|
|
13
|
+
className?: string;
|
|
14
|
+
}
|
|
15
|
+
export declare function TileGrid({ columns, children, className, }: TileGridProps): import("react/jsx-runtime").JSX.Element;
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
import "./TileGrid.css";
|
|
3
|
+
export function TileGrid(_a) {
|
|
4
|
+
var _b = _a.columns, columns = _b === void 0 ? 4 : _b, children = _a.children, _c = _a.className, className = _c === void 0 ? "" : _c;
|
|
5
|
+
var classNames = [
|
|
6
|
+
"dds-tile-grid",
|
|
7
|
+
"dds-tile-grid-".concat(columns),
|
|
8
|
+
className,
|
|
9
|
+
]
|
|
10
|
+
.filter(Boolean)
|
|
11
|
+
.join(" ");
|
|
12
|
+
return _jsx("div", { className: classNames, children: children });
|
|
13
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import type { Meta, StoryObj } from '@storybook/react';
|
|
2
|
+
import { TileGrid } from './TileGrid';
|
|
3
|
+
/**
|
|
4
|
+
* TileGrid lays out Tile components in a responsive CSS grid.
|
|
5
|
+
* It supports 3–6 columns and automatically adjusts to fewer columns on smaller screens.
|
|
6
|
+
*/
|
|
7
|
+
declare const meta: Meta<typeof TileGrid>;
|
|
8
|
+
export default meta;
|
|
9
|
+
type Story = StoryObj<typeof TileGrid>;
|
|
10
|
+
export declare const ThreeColumns: Story;
|
|
11
|
+
export declare const FourColumns: Story;
|
|
12
|
+
export declare const SixColumns: Story;
|
|
13
|
+
export declare const WithDescriptions: Story;
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
var __spreadArray = (this && this.__spreadArray) || function (to, from, pack) {
|
|
2
|
+
if (pack || arguments.length === 2) for (var i = 0, l = from.length, ar; i < l; i++) {
|
|
3
|
+
if (ar || !(i in from)) {
|
|
4
|
+
if (!ar) ar = Array.prototype.slice.call(from, 0, i);
|
|
5
|
+
ar[i] = from[i];
|
|
6
|
+
}
|
|
7
|
+
}
|
|
8
|
+
return to.concat(ar || Array.prototype.slice.call(from));
|
|
9
|
+
};
|
|
10
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
11
|
+
import { TileGrid } from './TileGrid';
|
|
12
|
+
import { Tile } from './Tile';
|
|
13
|
+
/**
|
|
14
|
+
* TileGrid lays out Tile components in a responsive CSS grid.
|
|
15
|
+
* It supports 3–6 columns and automatically adjusts to fewer columns on smaller screens.
|
|
16
|
+
*/
|
|
17
|
+
var meta = {
|
|
18
|
+
title: 'Components/TileGrid',
|
|
19
|
+
component: TileGrid,
|
|
20
|
+
tags: ['autodocs'],
|
|
21
|
+
argTypes: {
|
|
22
|
+
columns: {
|
|
23
|
+
control: { type: 'select' },
|
|
24
|
+
options: [3, 4, 5, 6],
|
|
25
|
+
description: 'Number of columns at full width.',
|
|
26
|
+
table: { defaultValue: { summary: '4' } },
|
|
27
|
+
},
|
|
28
|
+
children: {
|
|
29
|
+
control: false,
|
|
30
|
+
description: 'Grid content (typically Tile components).',
|
|
31
|
+
},
|
|
32
|
+
className: {
|
|
33
|
+
control: 'text',
|
|
34
|
+
description: 'Additional CSS classes.',
|
|
35
|
+
},
|
|
36
|
+
},
|
|
37
|
+
parameters: {
|
|
38
|
+
docs: {
|
|
39
|
+
description: {
|
|
40
|
+
component: "\nTileGrid is a responsive CSS grid container designed specifically for Tile components.\n\n## Responsive behavior\n\n| columns | Mobile (< 640px) | Tablet (\u2265 640px) | Desktop (\u2265 1024px) |\n|---------|-----------------|-----------------|-------------------|\n| 3 | 1 col | 2 col | 3 col |\n| 4 | 2 col | 2 col | 4 col |\n| 5 | 2 col | 3 col | 5 col |\n| 6 | 2 col | 3 col | 6 col |\n\n## Usage\n\n```tsx\n<TileGrid columns={4}>\n <Tile title=\"React\" icon={<ReactIcon />} href=\"/integrations/react\" />\n <Tile title=\"Vue\" icon={<VueIcon />} href=\"/integrations/vue\" />\n {/* \u2026more tiles */}\n</TileGrid>\n```\n ",
|
|
41
|
+
},
|
|
42
|
+
},
|
|
43
|
+
},
|
|
44
|
+
};
|
|
45
|
+
export default meta;
|
|
46
|
+
var DemoIcon = function () { return (_jsx("svg", { viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "1.5", strokeLinecap: "round", strokeLinejoin: "round", children: _jsx("path", { d: "M12 6.042A8.967 8.967 0 0 0 6 3.75c-1.052 0-2.062.18-3 .512v14.25A8.987 8.987 0 0 1 6 18c2.305 0 4.408.867 6 2.292m0-14.25a8.966 8.966 0 0 1 6-2.292c1.052 0 2.062.18 3 .512v14.25A8.987 8.987 0 0 0 18 18a8.967 8.967 0 0 0-6 2.292m0-14.25v14.25" }) })); };
|
|
47
|
+
var names8 = ['React', 'Vue', 'Angular', 'Svelte', 'Next.js', 'Nuxt', 'Astro', 'Remix'];
|
|
48
|
+
var names12 = __spreadArray(__spreadArray([], names8, true), ['Node.js', 'Deno', 'Bun', 'Express'], false);
|
|
49
|
+
export var ThreeColumns = {
|
|
50
|
+
render: function () { return (_jsx(TileGrid, { columns: 3, children: names8.map(function (name) { return (_jsx(Tile, { title: name, icon: _jsx(DemoIcon, {}), href: "/integrations/".concat(name.toLowerCase()) }, name)); }) })); },
|
|
51
|
+
parameters: {
|
|
52
|
+
docs: {
|
|
53
|
+
source: {
|
|
54
|
+
code: "<TileGrid columns={3}>\n {integrations.map((name) => (\n <Tile key={name} title={name} icon={<Icon />} href={`/integrations/${name}`} />\n ))}\n</TileGrid>",
|
|
55
|
+
},
|
|
56
|
+
},
|
|
57
|
+
},
|
|
58
|
+
};
|
|
59
|
+
export var FourColumns = {
|
|
60
|
+
render: function () { return (_jsx(TileGrid, { columns: 4, children: names12.map(function (name) { return (_jsx(Tile, { title: name, icon: _jsx(DemoIcon, {}), href: "/integrations/".concat(name.toLowerCase()) }, name)); }) })); },
|
|
61
|
+
parameters: {
|
|
62
|
+
docs: {
|
|
63
|
+
source: {
|
|
64
|
+
code: "<TileGrid columns={4}>\n {integrations.map((name) => (\n <Tile key={name} title={name} icon={<Icon />} href={`/integrations/${name}`} />\n ))}\n</TileGrid>",
|
|
65
|
+
},
|
|
66
|
+
},
|
|
67
|
+
},
|
|
68
|
+
};
|
|
69
|
+
export var SixColumns = {
|
|
70
|
+
render: function () { return (_jsx(TileGrid, { columns: 6, children: __spreadArray(__spreadArray([], names12, true), ['Postgres', 'MySQL', 'Redis', 'MongoDB', 'Kafka', 'RabbitMQ'], false).map(function (name) { return (_jsx(Tile, { title: name, icon: _jsx(DemoIcon, {}), href: "/integrations/".concat(name.toLowerCase()) }, name)); }) })); },
|
|
71
|
+
parameters: {
|
|
72
|
+
docs: {
|
|
73
|
+
source: {
|
|
74
|
+
code: "<TileGrid columns={6}>\n {integrations.map((name) => (\n <Tile key={name} title={name} icon={<Icon />} href={`/integrations/${name}`} />\n ))}\n</TileGrid>",
|
|
75
|
+
},
|
|
76
|
+
},
|
|
77
|
+
},
|
|
78
|
+
};
|
|
79
|
+
export var WithDescriptions = {
|
|
80
|
+
render: function () {
|
|
81
|
+
var skills = [
|
|
82
|
+
{ name: 'TypeScript', desc: 'Typed JavaScript' },
|
|
83
|
+
{ name: 'GraphQL', desc: 'Query language for APIs' },
|
|
84
|
+
{ name: 'Docker', desc: 'Container platform' },
|
|
85
|
+
{ name: 'Kubernetes', desc: 'Orchestration' },
|
|
86
|
+
{ name: 'Terraform', desc: 'Infrastructure as code' },
|
|
87
|
+
{ name: 'Postgres', desc: 'Relational database' },
|
|
88
|
+
{ name: 'Redis', desc: 'In-memory data store' },
|
|
89
|
+
{ name: 'Kafka', desc: 'Event streaming' },
|
|
90
|
+
];
|
|
91
|
+
return (_jsx(TileGrid, { columns: 4, children: skills.map(function (_a) {
|
|
92
|
+
var name = _a.name, desc = _a.desc;
|
|
93
|
+
return (_jsx(Tile, { title: name, icon: _jsx(DemoIcon, {}), description: desc, href: "/skills/".concat(name.toLowerCase()) }, name));
|
|
94
|
+
}) }));
|
|
95
|
+
},
|
|
96
|
+
};
|
package/dist/index.d.ts
CHANGED
|
@@ -15,4 +15,6 @@ export * from './components/Table';
|
|
|
15
15
|
export * from './components/Grid';
|
|
16
16
|
export * from './components/Breadcrumb';
|
|
17
17
|
export * from './components/Icon';
|
|
18
|
+
export * from './components/Tile';
|
|
19
|
+
export * from './components/TileGrid';
|
|
18
20
|
export * from './hooks/useKeyPress';
|
package/dist/index.js
CHANGED
|
@@ -16,5 +16,7 @@ export * from './components/Table';
|
|
|
16
16
|
export * from './components/Grid';
|
|
17
17
|
export * from './components/Breadcrumb';
|
|
18
18
|
export * from './components/Icon';
|
|
19
|
+
export * from './components/Tile';
|
|
20
|
+
export * from './components/TileGrid';
|
|
19
21
|
// Export hooks
|
|
20
22
|
export * from './hooks/useKeyPress';
|
package/dist/styles.css
CHANGED
|
@@ -287,6 +287,27 @@
|
|
|
287
287
|
--dds-table-cell-padding: 0.75rem 1rem;
|
|
288
288
|
--dds-table-focus-ring: #3b82f6; /* blue-500 */
|
|
289
289
|
|
|
290
|
+
/* Tile */
|
|
291
|
+
--dds-tile-padding: 0.875rem;
|
|
292
|
+
--dds-tile-radius: 0.375rem;
|
|
293
|
+
--dds-tile-border: #e5e7eb; /* gray-200 */
|
|
294
|
+
--dds-tile-border-hover: #d1d5db; /* gray-300 */
|
|
295
|
+
--dds-tile-bg: #ffffff;
|
|
296
|
+
--dds-tile-shadow-hover:
|
|
297
|
+
0 1px 3px 0 rgba(0, 0, 0, 0.1), 0 1px 2px -1px rgba(0, 0, 0, 0.1);
|
|
298
|
+
--dds-tile-icon-size: 1.25rem; /* 20px */
|
|
299
|
+
--dds-tile-icon-color: #6b7280; /* gray-500 */
|
|
300
|
+
--dds-tile-icon-gap: 0.75rem;
|
|
301
|
+
--dds-tile-title-size: 0.9375rem; /* 15px */
|
|
302
|
+
--dds-tile-title-color: #111827; /* gray-900 */
|
|
303
|
+
--dds-tile-desc-size: 0.8125rem; /* 13px */
|
|
304
|
+
--dds-tile-desc-color: #6b7280; /* gray-500 */
|
|
305
|
+
--dds-tile-arrow-color: #9ca3af; /* gray-400 */
|
|
306
|
+
--dds-tile-arrow-inset: 0.625rem;
|
|
307
|
+
|
|
308
|
+
/* TileGrid */
|
|
309
|
+
--dds-tile-grid-gap: 0.75rem;
|
|
310
|
+
|
|
290
311
|
/* Grid */
|
|
291
312
|
--dds-grid-gap-sm: 1rem; /* gap-4 */
|
|
292
313
|
--dds-grid-gap-md: 1.5rem; /* gap-6 */
|
|
@@ -486,6 +507,16 @@
|
|
|
486
507
|
--dds-table-header-active-text: #93c5fd; /* blue-300 */
|
|
487
508
|
--dds-table-focus-ring: #60a5fa; /* blue-400 */
|
|
488
509
|
|
|
510
|
+
/* Tile */
|
|
511
|
+
--dds-tile-border: #4b5563; /* gray-600 */
|
|
512
|
+
--dds-tile-border-hover: #9ca3af; /* gray-400 */
|
|
513
|
+
--dds-tile-bg: transparent;
|
|
514
|
+
--dds-tile-shadow-hover: none;
|
|
515
|
+
--dds-tile-icon-color: #9ca3af; /* gray-400 */
|
|
516
|
+
--dds-tile-title-color: #e5e7eb; /* gray-200 */
|
|
517
|
+
--dds-tile-desc-color: #9ca3af; /* gray-400 */
|
|
518
|
+
--dds-tile-arrow-color: #6b7280; /* gray-500 */
|
|
519
|
+
|
|
489
520
|
/* Grid */
|
|
490
521
|
--dds-grid-divider-color: #4b5563; /* gray-600 */
|
|
491
522
|
|
|
@@ -671,6 +702,16 @@
|
|
|
671
702
|
--dds-table-header-active-text: #93c5fd;
|
|
672
703
|
--dds-table-focus-ring: #60a5fa;
|
|
673
704
|
|
|
705
|
+
/* Tile */
|
|
706
|
+
--dds-tile-border: #4b5563; /* gray-600 */
|
|
707
|
+
--dds-tile-border-hover: #9ca3af; /* gray-400 */
|
|
708
|
+
--dds-tile-bg: transparent;
|
|
709
|
+
--dds-tile-shadow-hover: none;
|
|
710
|
+
--dds-tile-icon-color: #9ca3af; /* gray-400 */
|
|
711
|
+
--dds-tile-title-color: #e5e7eb; /* gray-200 */
|
|
712
|
+
--dds-tile-desc-color: #9ca3af; /* gray-400 */
|
|
713
|
+
--dds-tile-arrow-color: #6b7280; /* gray-500 */
|
|
714
|
+
|
|
674
715
|
/* Grid */
|
|
675
716
|
--dds-grid-divider-color: #4b5563; /* gray-600 */
|
|
676
717
|
|
|
@@ -838,6 +879,16 @@
|
|
|
838
879
|
--dds-table-header-active-text: #93c5fd;
|
|
839
880
|
--dds-table-focus-ring: #60a5fa;
|
|
840
881
|
|
|
882
|
+
/* Tile */
|
|
883
|
+
--dds-tile-border: #4b5563; /* gray-600 */
|
|
884
|
+
--dds-tile-border-hover: #9ca3af; /* gray-400 */
|
|
885
|
+
--dds-tile-bg: transparent;
|
|
886
|
+
--dds-tile-shadow-hover: none;
|
|
887
|
+
--dds-tile-icon-color: #9ca3af; /* gray-400 */
|
|
888
|
+
--dds-tile-title-color: #e5e7eb; /* gray-200 */
|
|
889
|
+
--dds-tile-desc-color: #9ca3af; /* gray-400 */
|
|
890
|
+
--dds-tile-arrow-color: #6b7280; /* gray-500 */
|
|
891
|
+
|
|
841
892
|
/* Breadcrumb */
|
|
842
893
|
--dds-breadcrumb-link-color: #9ca3af; /* gray-400 */
|
|
843
894
|
--dds-breadcrumb-link-color-hover: #f9fafb; /* gray-50 */
|
|
@@ -1095,6 +1146,7 @@ pre[class*="language-"] {
|
|
|
1095
1146
|
color: var(--dds-callout-course-title);
|
|
1096
1147
|
}
|
|
1097
1148
|
a.no-text-decoration {
|
|
1149
|
+
display: block;
|
|
1098
1150
|
text-decoration: none;
|
|
1099
1151
|
}
|
|
1100
1152
|
.dds-card {
|
|
@@ -2428,4 +2480,170 @@ a.dds-breadcrumb-link:hover {
|
|
|
2428
2480
|
width: 100%;
|
|
2429
2481
|
height: 100%;
|
|
2430
2482
|
}
|
|
2483
|
+
a.dds-tile-link {
|
|
2484
|
+
display: block;
|
|
2485
|
+
text-decoration: none;
|
|
2486
|
+
}
|
|
2487
|
+
.dds-tile {
|
|
2488
|
+
padding: var(--dds-tile-padding);
|
|
2489
|
+
border-radius: var(--dds-tile-radius);
|
|
2490
|
+
border: 1px solid var(--dds-tile-border);
|
|
2491
|
+
background-color: var(--dds-tile-bg);
|
|
2492
|
+
position: relative;
|
|
2493
|
+
box-sizing: border-box;
|
|
2494
|
+
height: 100%;
|
|
2495
|
+
}
|
|
2496
|
+
.dds-tile-clickable {
|
|
2497
|
+
cursor: pointer;
|
|
2498
|
+
transition: var(--dds-transition-shadow), border-color 150ms ease-in-out;
|
|
2499
|
+
}
|
|
2500
|
+
.dds-tile-clickable:hover {
|
|
2501
|
+
box-shadow: var(--dds-tile-shadow-hover);
|
|
2502
|
+
border-color: var(--dds-tile-border-hover);
|
|
2503
|
+
}
|
|
2504
|
+
/* Layout: icon on left, text on right */
|
|
2505
|
+
.dds-tile-body {
|
|
2506
|
+
display: flex;
|
|
2507
|
+
align-items: center;
|
|
2508
|
+
gap: var(--dds-tile-icon-gap);
|
|
2509
|
+
}
|
|
2510
|
+
.dds-tile-icon {
|
|
2511
|
+
display: flex;
|
|
2512
|
+
align-items: center;
|
|
2513
|
+
justify-content: center;
|
|
2514
|
+
flex-shrink: 0;
|
|
2515
|
+
width: var(--dds-tile-icon-size);
|
|
2516
|
+
height: var(--dds-tile-icon-size);
|
|
2517
|
+
color: var(--dds-tile-icon-color);
|
|
2518
|
+
}
|
|
2519
|
+
.dds-tile-icon > * {
|
|
2520
|
+
width: 100%;
|
|
2521
|
+
height: 100%;
|
|
2522
|
+
}
|
|
2523
|
+
/* Text stack */
|
|
2524
|
+
.dds-tile-text {
|
|
2525
|
+
display: flex;
|
|
2526
|
+
flex-direction: column;
|
|
2527
|
+
gap: 0.125rem;
|
|
2528
|
+
min-width: 0;
|
|
2529
|
+
}
|
|
2530
|
+
.dds-tile-title {
|
|
2531
|
+
font-size: var(--dds-tile-title-size);
|
|
2532
|
+
font-weight: var(--dds-font-semibold);
|
|
2533
|
+
color: var(--dds-tile-title-color);
|
|
2534
|
+
line-height: var(--dds-line-height-tight);
|
|
2535
|
+
white-space: nowrap;
|
|
2536
|
+
overflow: hidden;
|
|
2537
|
+
text-overflow: ellipsis;
|
|
2538
|
+
}
|
|
2539
|
+
.dds-tile-desc {
|
|
2540
|
+
display: block;
|
|
2541
|
+
font-size: var(--dds-tile-desc-size);
|
|
2542
|
+
color: var(--dds-tile-desc-color);
|
|
2543
|
+
line-height: var(--dds-line-height-relaxed);
|
|
2544
|
+
white-space: nowrap;
|
|
2545
|
+
overflow: hidden;
|
|
2546
|
+
text-overflow: ellipsis;
|
|
2547
|
+
}
|
|
2548
|
+
/* Arrow */
|
|
2549
|
+
.dds-tile-has-arrow {
|
|
2550
|
+
padding-right: calc(var(--dds-tile-padding) + 1.125rem);
|
|
2551
|
+
}
|
|
2552
|
+
[dir="rtl"] .dds-tile-has-arrow {
|
|
2553
|
+
padding-right: var(--dds-tile-padding);
|
|
2554
|
+
padding-left: calc(var(--dds-tile-padding) + 1.125rem);
|
|
2555
|
+
}
|
|
2556
|
+
.dds-tile-arrow {
|
|
2557
|
+
position: absolute;
|
|
2558
|
+
bottom: var(--dds-tile-arrow-inset);
|
|
2559
|
+
right: var(--dds-tile-arrow-inset);
|
|
2560
|
+
color: var(--dds-tile-arrow-color);
|
|
2561
|
+
display: flex;
|
|
2562
|
+
align-items: center;
|
|
2563
|
+
justify-content: center;
|
|
2564
|
+
pointer-events: none;
|
|
2565
|
+
}
|
|
2566
|
+
[dir="rtl"] .dds-tile-arrow {
|
|
2567
|
+
right: auto;
|
|
2568
|
+
left: var(--dds-tile-arrow-inset);
|
|
2569
|
+
transform: scaleX(-1);
|
|
2570
|
+
}
|
|
2571
|
+
@keyframes dds-tile-arrow-nudge {
|
|
2572
|
+
0% { transform: translateX(0); }
|
|
2573
|
+
40% { transform: translateX(4px); }
|
|
2574
|
+
70% { transform: translateX(-2px); }
|
|
2575
|
+
100% { transform: translateX(0); }
|
|
2576
|
+
}
|
|
2577
|
+
@keyframes dds-tile-arrow-nudge-rtl {
|
|
2578
|
+
0% { transform: scaleX(-1) translateX(0); }
|
|
2579
|
+
40% { transform: scaleX(-1) translateX(4px); }
|
|
2580
|
+
70% { transform: scaleX(-1) translateX(-2px); }
|
|
2581
|
+
100% { transform: scaleX(-1) translateX(0); }
|
|
2582
|
+
}
|
|
2583
|
+
.dds-tile-clickable:hover .dds-tile-arrow {
|
|
2584
|
+
animation: dds-tile-arrow-nudge 0.4s cubic-bezier(0.34, 1.56, 0.64, 1) forwards;
|
|
2585
|
+
}
|
|
2586
|
+
[dir="rtl"] .dds-tile-clickable:hover .dds-tile-arrow {
|
|
2587
|
+
animation: dds-tile-arrow-nudge-rtl 0.4s cubic-bezier(0.34, 1.56, 0.64, 1) forwards;
|
|
2588
|
+
}
|
|
2589
|
+
.dds-tile-grid {
|
|
2590
|
+
display: grid;
|
|
2591
|
+
gap: var(--dds-tile-grid-gap);
|
|
2592
|
+
}
|
|
2593
|
+
/* 3 columns: 1 → 2 → 3 */
|
|
2594
|
+
.dds-tile-grid-3 {
|
|
2595
|
+
grid-template-columns: 1fr;
|
|
2596
|
+
}
|
|
2597
|
+
@media (min-width: 640px) {
|
|
2598
|
+
.dds-tile-grid-3 {
|
|
2599
|
+
grid-template-columns: repeat(2, 1fr);
|
|
2600
|
+
}
|
|
2601
|
+
}
|
|
2602
|
+
@media (min-width: 1024px) {
|
|
2603
|
+
.dds-tile-grid-3 {
|
|
2604
|
+
grid-template-columns: repeat(3, 1fr);
|
|
2605
|
+
}
|
|
2606
|
+
}
|
|
2607
|
+
/* 4 columns: 2 → 2 → 4 */
|
|
2608
|
+
.dds-tile-grid-4 {
|
|
2609
|
+
grid-template-columns: repeat(2, 1fr);
|
|
2610
|
+
}
|
|
2611
|
+
@media (min-width: 768px) {
|
|
2612
|
+
.dds-tile-grid-4 {
|
|
2613
|
+
grid-template-columns: repeat(2, 1fr);
|
|
2614
|
+
}
|
|
2615
|
+
}
|
|
2616
|
+
@media (min-width: 1024px) {
|
|
2617
|
+
.dds-tile-grid-4 {
|
|
2618
|
+
grid-template-columns: repeat(4, 1fr);
|
|
2619
|
+
}
|
|
2620
|
+
}
|
|
2621
|
+
/* 5 columns: 2 → 3 → 5 */
|
|
2622
|
+
.dds-tile-grid-5 {
|
|
2623
|
+
grid-template-columns: repeat(2, 1fr);
|
|
2624
|
+
}
|
|
2625
|
+
@media (min-width: 640px) {
|
|
2626
|
+
.dds-tile-grid-5 {
|
|
2627
|
+
grid-template-columns: repeat(3, 1fr);
|
|
2628
|
+
}
|
|
2629
|
+
}
|
|
2630
|
+
@media (min-width: 1024px) {
|
|
2631
|
+
.dds-tile-grid-5 {
|
|
2632
|
+
grid-template-columns: repeat(5, 1fr);
|
|
2633
|
+
}
|
|
2634
|
+
}
|
|
2635
|
+
/* 6 columns: 2 → 3 → 6 */
|
|
2636
|
+
.dds-tile-grid-6 {
|
|
2637
|
+
grid-template-columns: repeat(2, 1fr);
|
|
2638
|
+
}
|
|
2639
|
+
@media (min-width: 640px) {
|
|
2640
|
+
.dds-tile-grid-6 {
|
|
2641
|
+
grid-template-columns: repeat(3, 1fr);
|
|
2642
|
+
}
|
|
2643
|
+
}
|
|
2644
|
+
@media (min-width: 1024px) {
|
|
2645
|
+
.dds-tile-grid-6 {
|
|
2646
|
+
grid-template-columns: repeat(6, 1fr);
|
|
2647
|
+
}
|
|
2648
|
+
}
|
|
2431
2649
|
|