@eduardoalvarez/arrecife 0.10.0 → 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.
- package/CHANGELOG.md +16 -0
- package/README.md +12 -1
- package/dist/brand/index.cjs +9 -3
- package/dist/brand/index.d.cts +45 -6
- package/dist/brand/index.d.ts +45 -6
- package/dist/brand/index.js +1 -1
- package/dist/chart/index.cjs +26 -4
- package/dist/chart/index.d.cts +19 -3
- package/dist/chart/index.d.ts +19 -3
- package/dist/chart/index.js +26 -4
- package/dist/{chunk-IIT3YLYN.js → chunk-RKGKO2TW.js} +10 -4
- package/dist/index.cjs +72 -20
- package/dist/index.d.cts +66 -4
- package/dist/index.d.ts +66 -4
- package/dist/index.js +65 -19
- package/llms.txt +36 -4
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,21 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## [0.11.0](https://github.com/Proskynete/arrecife/compare/v0.10.0...v0.11.0) (2026-09-11)
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
### 🚀 Novedades
|
|
7
|
+
|
|
8
|
+
* **brand:** the fin can follow the theme with background="auto" ([3021f24](https://github.com/Proskynete/arrecife/commit/3021f24575c6f9422b7f322bf825a878a735306d))
|
|
9
|
+
* **chart:** valueMax sets the top of the value axis ([ccebe24](https://github.com/Proskynete/arrecife/commit/ccebe24361ce4004916b433819ddf7f53f89907a))
|
|
10
|
+
* **components:** CourseCard takes a cover and a closing row ([a3c73d0](https://github.com/Proskynete/arrecife/commit/a3c73d0b21edffc16c2965475fce9de6e1502926))
|
|
11
|
+
* **components:** PageHeader lets the screen pick the title scale ([99bd553](https://github.com/Proskynete/arrecife/commit/99bd55376992109ea90c8eaedc435ffcadffdb0e))
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
### 📚 Documentación
|
|
15
|
+
|
|
16
|
+
* four decisions from cursos adopting the whole library ([2a712b8](https://github.com/Proskynete/arrecife/commit/2a712b871edd73f63fddfd2733279bf3e2834e07))
|
|
17
|
+
* **readme:** background="auto", valueMax, and the story count ([149442c](https://github.com/Proskynete/arrecife/commit/149442cb2748da9cb06b3df23254cba46aae378a))
|
|
18
|
+
|
|
3
19
|
## [0.10.0](https://github.com/Proskynete/arrecife/compare/v0.9.0...v0.10.0) (2026-09-09)
|
|
4
20
|
|
|
5
21
|
|
package/README.md
CHANGED
|
@@ -588,6 +588,11 @@ vertical={false}`, two axes with the line and the tick off, a `type="natural"`
|
|
|
588
588
|
and a `strokeWidth` — which `cursos` wrote four times, once per chart. None of
|
|
589
589
|
that is a decision the project made. See `docs/decisions/0.8.md` § 43.
|
|
590
590
|
|
|
591
|
+
A chart of a quantity with a ceiling passes it: `valueMax={100}` on a percentage.
|
|
592
|
+
Without it the value axis ends at the largest datum, and a horizontal ranking —
|
|
593
|
+
whose value axis is hidden — draws a course watched to 40 % as a full bar. See
|
|
594
|
+
`docs/decisions/0.11.md` § 58.
|
|
595
|
+
|
|
591
596
|
`check:exports` verifies that the five portable ones — `./tokens`, `./theme`,
|
|
592
597
|
`./variants`, `./og` and `./shiki` — bring no React into the published `dist/`,
|
|
593
598
|
**by following the relative imports**. Without that the check was worthless: with `treeshake`
|
|
@@ -603,7 +608,7 @@ chunk imports it.
|
|
|
603
608
|
| `pnpm typecheck` | `tsc --noEmit` |
|
|
604
609
|
| `pnpm lint` | ESLint, including the ban on literal hexes outside `tokens.ts` |
|
|
605
610
|
| `pnpm check:tokens` | fails if `src/tokens/` imports anything from outside |
|
|
606
|
-
| `pnpm test` | compiles Tailwind and runs axe over the
|
|
611
|
+
| `pnpm test` | compiles Tailwind and runs axe over the 248 stories, in both modes |
|
|
607
612
|
| `pnpm check:exports` | verifies that `dist/` holds what `exports` promises |
|
|
608
613
|
| `pnpm check:release` | validates `release-please-config.json` against the official schema |
|
|
609
614
|
| `pnpm storybook` | generates the tokens and serves Storybook on 6006 |
|
|
@@ -1047,6 +1052,12 @@ adding a line to the catalog.
|
|
|
1047
1052
|
Pixel analysis confirms it — 94 % of `fin-foam.png` is `#EDF4F3`, which is the
|
|
1048
1053
|
foam token.
|
|
1049
1054
|
|
|
1055
|
+
On a site that switches theme, `background="auto"` renders both and the `light:`
|
|
1056
|
+
variant shows the one that reads, so no call site has to know which theme is on.
|
|
1057
|
+
A background that does not follow the theme — a dark panel on a light page —
|
|
1058
|
+
still names itself: `auto` reads the page, not the panel. See
|
|
1059
|
+
`docs/decisions/0.11.md` § 60.
|
|
1060
|
+
|
|
1050
1061
|
**Rule 5 as API.** The wordmark comes from `naming.wordmark` and always reads
|
|
1051
1062
|
«Eduardo Álvarez». There is no prop that changes that text, and Tiburoncín never
|
|
1052
1063
|
appears written inside the logo.
|
package/dist/brand/index.cjs
CHANGED
|
@@ -168,18 +168,24 @@ function Isotype({
|
|
|
168
168
|
className,
|
|
169
169
|
...props
|
|
170
170
|
}) {
|
|
171
|
-
const
|
|
172
|
-
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
171
|
+
const fin = (file, mode) => /* @__PURE__ */ jsxRuntime.jsx(
|
|
173
172
|
"img",
|
|
174
173
|
{
|
|
175
174
|
src: `${basePath}/${file}`,
|
|
176
175
|
alt,
|
|
177
176
|
width: 147,
|
|
178
177
|
height: 111,
|
|
179
|
-
className: cn("h-8 w-auto select-none", className),
|
|
178
|
+
className: cn("h-8 w-auto select-none", className, mode),
|
|
180
179
|
...props
|
|
181
180
|
}
|
|
182
181
|
);
|
|
182
|
+
if (background === "auto") {
|
|
183
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
|
|
184
|
+
fin(fins.foam, "light:hidden"),
|
|
185
|
+
fin(fins.color, "not-light:hidden")
|
|
186
|
+
] });
|
|
187
|
+
}
|
|
188
|
+
return fin(background === "dark" ? fins.foam : fins.color);
|
|
183
189
|
}
|
|
184
190
|
function Logo({
|
|
185
191
|
background = "dark",
|
package/dist/brand/index.d.cts
CHANGED
|
@@ -3,21 +3,60 @@ export { A as ASSETS_PATH, a as Fin, f as faceList, b as faceUsage, c as faces,
|
|
|
3
3
|
import * as react from 'react';
|
|
4
4
|
import { ComponentPropsWithoutRef } from 'react';
|
|
5
5
|
|
|
6
|
+
/**
|
|
7
|
+
* Which background the fin sits on, or `auto` to let the theme decide.
|
|
8
|
+
*
|
|
9
|
+
* It is its own type and not a third member of `Background`, because
|
|
10
|
+
* `Background` is also what the OG templates read, and Satori has no CSS to
|
|
11
|
+
* resolve `auto` with: a template knows its mode and has to say it.
|
|
12
|
+
*/
|
|
13
|
+
type IsotypeBackground = Background | 'auto';
|
|
6
14
|
type IsotypeProps = Omit<ComponentPropsWithoutRef<'img'>, 'src' | 'alt'> & {
|
|
7
15
|
/**
|
|
8
|
-
* Which background it sits on
|
|
9
|
-
*
|
|
10
|
-
*
|
|
16
|
+
* Which background it sits on, or `auto` on a site that switches theme: both
|
|
17
|
+
* fins are rendered and CSS shows the one that reads. Deciding is mandatory
|
|
18
|
+
* even though it has a default: the fin's body is nearly black, so the
|
|
19
|
+
* two-blue variant disappears over abyss.
|
|
20
|
+
*
|
|
21
|
+
* Use `dark` or `light` when the background does not follow the theme. A
|
|
22
|
+
* panel that stays dark in both modes is a fixed background, and `auto` would
|
|
23
|
+
* pick the page's fin instead of the panel's.
|
|
11
24
|
*/
|
|
12
|
-
background?:
|
|
25
|
+
background?: IsotypeBackground | undefined;
|
|
13
26
|
basePath?: string | undefined;
|
|
14
27
|
/** Alt text. Empty when the isotype accompanies text that already names it. */
|
|
15
28
|
alt?: string | undefined;
|
|
16
29
|
};
|
|
30
|
+
/**
|
|
31
|
+
* The fin, in the variant its background asks for.
|
|
32
|
+
*
|
|
33
|
+
* `auto` renders both and lets the `light:` variant choose, which is what
|
|
34
|
+
* `ThemeToggle` already does with its two icons and for the same reason: the
|
|
35
|
+
* server does not know the theme, and a fin picked in JavaScript would be wrong
|
|
36
|
+
* on the first paint half the time. `cursos` had written exactly this by hand
|
|
37
|
+
* after a surface changed mode and its fin vanished — not looked wrong: vanished,
|
|
38
|
+
* with no error and no gap in the layout. See `docs/decisions/0.11.md` § 60.
|
|
39
|
+
*
|
|
40
|
+
* Dark is the system default, so the foam fin is the one shown unless
|
|
41
|
+
* `data-theme="light"` says otherwise, and the two-blue one hides everywhere
|
|
42
|
+
* else. `not-light:hidden` rather than `hidden light:block`: the visible fin
|
|
43
|
+
* keeps whatever `display` the call site gave it.
|
|
44
|
+
*
|
|
45
|
+
* What it costs: the hidden image is still downloaded, 14 or 21 KB. And with
|
|
46
|
+
* `auto` the props land on BOTH `<img>` — an `id` or a `ref` included — so a
|
|
47
|
+
* call site that needs to reach the one image passes the background it is on.
|
|
48
|
+
* The `alt` does not double: an image with `display: none` is out of the
|
|
49
|
+
* accessibility tree.
|
|
50
|
+
*/
|
|
17
51
|
declare function Isotype({ background, basePath, alt, className, ...props }: IsotypeProps): react.JSX.Element;
|
|
18
52
|
|
|
19
53
|
type LogoProps = Omit<ComponentPropsWithoutRef<'span'>, 'children'> & {
|
|
20
|
-
|
|
54
|
+
/**
|
|
55
|
+
* The background the logo sits on, handed to its fin. `auto` follows the
|
|
56
|
+
* theme — see `Isotype`. The wordmark needs no help: it is `textPrimary`, which
|
|
57
|
+
* already follows the mode.
|
|
58
|
+
*/
|
|
59
|
+
background?: IsotypeBackground | undefined;
|
|
21
60
|
basePath?: string | undefined;
|
|
22
61
|
/** Hides the wordmark and leaves only the fin, for very narrow bars. */
|
|
23
62
|
isotypeOnly?: boolean | undefined;
|
|
@@ -69,4 +108,4 @@ type MascotFaceProps = Base & {
|
|
|
69
108
|
*/
|
|
70
109
|
declare function MascotFace({ expression, basePath, alt, className, ...props }: MascotFaceProps): react.JSX.Element;
|
|
71
110
|
|
|
72
|
-
export { Background, Face, Isotype, type IsotypeProps, Logo, type LogoProps, Mascot, MascotFace, type MascotFaceProps, type MascotProps, Pose };
|
|
111
|
+
export { Background, Face, Isotype, type IsotypeBackground, type IsotypeProps, Logo, type LogoProps, Mascot, MascotFace, type MascotFaceProps, type MascotProps, Pose };
|
package/dist/brand/index.d.ts
CHANGED
|
@@ -3,21 +3,60 @@ export { A as ASSETS_PATH, a as Fin, f as faceList, b as faceUsage, c as faces,
|
|
|
3
3
|
import * as react from 'react';
|
|
4
4
|
import { ComponentPropsWithoutRef } from 'react';
|
|
5
5
|
|
|
6
|
+
/**
|
|
7
|
+
* Which background the fin sits on, or `auto` to let the theme decide.
|
|
8
|
+
*
|
|
9
|
+
* It is its own type and not a third member of `Background`, because
|
|
10
|
+
* `Background` is also what the OG templates read, and Satori has no CSS to
|
|
11
|
+
* resolve `auto` with: a template knows its mode and has to say it.
|
|
12
|
+
*/
|
|
13
|
+
type IsotypeBackground = Background | 'auto';
|
|
6
14
|
type IsotypeProps = Omit<ComponentPropsWithoutRef<'img'>, 'src' | 'alt'> & {
|
|
7
15
|
/**
|
|
8
|
-
* Which background it sits on
|
|
9
|
-
*
|
|
10
|
-
*
|
|
16
|
+
* Which background it sits on, or `auto` on a site that switches theme: both
|
|
17
|
+
* fins are rendered and CSS shows the one that reads. Deciding is mandatory
|
|
18
|
+
* even though it has a default: the fin's body is nearly black, so the
|
|
19
|
+
* two-blue variant disappears over abyss.
|
|
20
|
+
*
|
|
21
|
+
* Use `dark` or `light` when the background does not follow the theme. A
|
|
22
|
+
* panel that stays dark in both modes is a fixed background, and `auto` would
|
|
23
|
+
* pick the page's fin instead of the panel's.
|
|
11
24
|
*/
|
|
12
|
-
background?:
|
|
25
|
+
background?: IsotypeBackground | undefined;
|
|
13
26
|
basePath?: string | undefined;
|
|
14
27
|
/** Alt text. Empty when the isotype accompanies text that already names it. */
|
|
15
28
|
alt?: string | undefined;
|
|
16
29
|
};
|
|
30
|
+
/**
|
|
31
|
+
* The fin, in the variant its background asks for.
|
|
32
|
+
*
|
|
33
|
+
* `auto` renders both and lets the `light:` variant choose, which is what
|
|
34
|
+
* `ThemeToggle` already does with its two icons and for the same reason: the
|
|
35
|
+
* server does not know the theme, and a fin picked in JavaScript would be wrong
|
|
36
|
+
* on the first paint half the time. `cursos` had written exactly this by hand
|
|
37
|
+
* after a surface changed mode and its fin vanished — not looked wrong: vanished,
|
|
38
|
+
* with no error and no gap in the layout. See `docs/decisions/0.11.md` § 60.
|
|
39
|
+
*
|
|
40
|
+
* Dark is the system default, so the foam fin is the one shown unless
|
|
41
|
+
* `data-theme="light"` says otherwise, and the two-blue one hides everywhere
|
|
42
|
+
* else. `not-light:hidden` rather than `hidden light:block`: the visible fin
|
|
43
|
+
* keeps whatever `display` the call site gave it.
|
|
44
|
+
*
|
|
45
|
+
* What it costs: the hidden image is still downloaded, 14 or 21 KB. And with
|
|
46
|
+
* `auto` the props land on BOTH `<img>` — an `id` or a `ref` included — so a
|
|
47
|
+
* call site that needs to reach the one image passes the background it is on.
|
|
48
|
+
* The `alt` does not double: an image with `display: none` is out of the
|
|
49
|
+
* accessibility tree.
|
|
50
|
+
*/
|
|
17
51
|
declare function Isotype({ background, basePath, alt, className, ...props }: IsotypeProps): react.JSX.Element;
|
|
18
52
|
|
|
19
53
|
type LogoProps = Omit<ComponentPropsWithoutRef<'span'>, 'children'> & {
|
|
20
|
-
|
|
54
|
+
/**
|
|
55
|
+
* The background the logo sits on, handed to its fin. `auto` follows the
|
|
56
|
+
* theme — see `Isotype`. The wordmark needs no help: it is `textPrimary`, which
|
|
57
|
+
* already follows the mode.
|
|
58
|
+
*/
|
|
59
|
+
background?: IsotypeBackground | undefined;
|
|
21
60
|
basePath?: string | undefined;
|
|
22
61
|
/** Hides the wordmark and leaves only the fin, for very narrow bars. */
|
|
23
62
|
isotypeOnly?: boolean | undefined;
|
|
@@ -69,4 +108,4 @@ type MascotFaceProps = Base & {
|
|
|
69
108
|
*/
|
|
70
109
|
declare function MascotFace({ expression, basePath, alt, className, ...props }: MascotFaceProps): react.JSX.Element;
|
|
71
110
|
|
|
72
|
-
export { Background, Face, Isotype, type IsotypeProps, Logo, type LogoProps, Mascot, MascotFace, type MascotFaceProps, type MascotProps, Pose };
|
|
111
|
+
export { Background, Face, Isotype, type IsotypeBackground, type IsotypeProps, Logo, type LogoProps, Mascot, MascotFace, type MascotFaceProps, type MascotProps, Pose };
|
package/dist/brand/index.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
'use client';
|
|
2
|
-
export { Isotype, Logo, Mascot, MascotFace } from '../chunk-
|
|
2
|
+
export { Isotype, Logo, Mascot, MascotFace } from '../chunk-RKGKO2TW.js';
|
|
3
3
|
import '../chunk-FAAGZG7A.js';
|
|
4
4
|
export { ASSETS_PATH, faceList, faceUsage, faces, fins, poseList, poses } from '../chunk-CKRSQPTX.js';
|
|
5
5
|
import '../chunk-FGFNK72B.js';
|
package/dist/chart/index.cjs
CHANGED
|
@@ -300,6 +300,7 @@ var color = (series2, index) => series2.color ?? seriesColor(index);
|
|
|
300
300
|
var MARGIN = { left: 0, right: 12, top: 8, bottom: 0 };
|
|
301
301
|
var AXIS = { tickLine: false, axisLine: false };
|
|
302
302
|
var tickOf = (format) => format ? { tickFormatter: format } : {};
|
|
303
|
+
var domainOf = (max) => max === void 0 ? {} : { domain: [0, max] };
|
|
303
304
|
var legendOf = (series2, legend) => legend ?? series2.length > 1 ? /* @__PURE__ */ jsxRuntime.jsx(ChartLegend, { content: /* @__PURE__ */ jsxRuntime.jsx(ChartLegendContent, {}) }, "legend") : null;
|
|
304
305
|
var tooltipOf = (formatter) => /* @__PURE__ */ jsxRuntime.jsx(
|
|
305
306
|
ChartTooltip,
|
|
@@ -316,6 +317,7 @@ function AreaChart({
|
|
|
316
317
|
formatter,
|
|
317
318
|
xTickFormatter,
|
|
318
319
|
yTickFormatter,
|
|
320
|
+
valueMax,
|
|
319
321
|
legend,
|
|
320
322
|
stacked = false,
|
|
321
323
|
...container
|
|
@@ -328,7 +330,7 @@ function AreaChart({
|
|
|
328
330
|
] }, s.key)) }),
|
|
329
331
|
/* @__PURE__ */ jsxRuntime.jsx(recharts.CartesianGrid, { vertical: false }),
|
|
330
332
|
/* @__PURE__ */ jsxRuntime.jsx(recharts.XAxis, { dataKey: xKey, ...AXIS, tickMargin: 8, ...tickOf(xTickFormatter) }),
|
|
331
|
-
/* @__PURE__ */ jsxRuntime.jsx(recharts.YAxis, { ...AXIS, width: 40, ...tickOf(yTickFormatter) }),
|
|
333
|
+
/* @__PURE__ */ jsxRuntime.jsx(recharts.YAxis, { ...AXIS, width: 40, ...tickOf(yTickFormatter), ...domainOf(valueMax) }),
|
|
332
334
|
tooltipOf(formatter),
|
|
333
335
|
legendOf(series2, legend),
|
|
334
336
|
series2.map((s, i) => /* @__PURE__ */ jsxRuntime.jsx(
|
|
@@ -354,6 +356,7 @@ function BarChart({
|
|
|
354
356
|
formatter,
|
|
355
357
|
xTickFormatter,
|
|
356
358
|
yTickFormatter,
|
|
359
|
+
valueMax,
|
|
357
360
|
legend,
|
|
358
361
|
orientation = "vertical",
|
|
359
362
|
stacked = false,
|
|
@@ -380,7 +383,16 @@ function BarChart({
|
|
|
380
383
|
},
|
|
381
384
|
"category"
|
|
382
385
|
),
|
|
383
|
-
/* @__PURE__ */ jsxRuntime.jsx(
|
|
386
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
387
|
+
recharts.XAxis,
|
|
388
|
+
{
|
|
389
|
+
type: "number",
|
|
390
|
+
hide: true,
|
|
391
|
+
...tickOf(yTickFormatter),
|
|
392
|
+
...domainOf(valueMax)
|
|
393
|
+
},
|
|
394
|
+
"value"
|
|
395
|
+
)
|
|
384
396
|
] : [
|
|
385
397
|
/* @__PURE__ */ jsxRuntime.jsx(
|
|
386
398
|
recharts.XAxis,
|
|
@@ -392,7 +404,16 @@ function BarChart({
|
|
|
392
404
|
},
|
|
393
405
|
"category"
|
|
394
406
|
),
|
|
395
|
-
/* @__PURE__ */ jsxRuntime.jsx(
|
|
407
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
408
|
+
recharts.YAxis,
|
|
409
|
+
{
|
|
410
|
+
...AXIS,
|
|
411
|
+
width: 40,
|
|
412
|
+
...tickOf(yTickFormatter),
|
|
413
|
+
...domainOf(valueMax)
|
|
414
|
+
},
|
|
415
|
+
"value"
|
|
416
|
+
)
|
|
396
417
|
],
|
|
397
418
|
tooltipOf(formatter),
|
|
398
419
|
legendOf(series2, legend),
|
|
@@ -419,13 +440,14 @@ function LineChart({
|
|
|
419
440
|
formatter,
|
|
420
441
|
xTickFormatter,
|
|
421
442
|
yTickFormatter,
|
|
443
|
+
valueMax,
|
|
422
444
|
legend,
|
|
423
445
|
...container
|
|
424
446
|
}) {
|
|
425
447
|
return /* @__PURE__ */ jsxRuntime.jsx(ChartContainer, { ...container, children: /* @__PURE__ */ jsxRuntime.jsxs(recharts.LineChart, { data: rows(data), margin: MARGIN, children: [
|
|
426
448
|
/* @__PURE__ */ jsxRuntime.jsx(recharts.CartesianGrid, { vertical: false }),
|
|
427
449
|
/* @__PURE__ */ jsxRuntime.jsx(recharts.XAxis, { dataKey: xKey, ...AXIS, tickMargin: 8, ...tickOf(xTickFormatter) }),
|
|
428
|
-
/* @__PURE__ */ jsxRuntime.jsx(recharts.YAxis, { ...AXIS, width: 40, ...tickOf(yTickFormatter) }),
|
|
450
|
+
/* @__PURE__ */ jsxRuntime.jsx(recharts.YAxis, { ...AXIS, width: 40, ...tickOf(yTickFormatter), ...domainOf(valueMax) }),
|
|
429
451
|
tooltipOf(formatter),
|
|
430
452
|
legendOf(series2, legend),
|
|
431
453
|
series2.map((s, i) => /* @__PURE__ */ jsxRuntime.jsx(
|
package/dist/chart/index.d.cts
CHANGED
|
@@ -187,6 +187,22 @@ type SeriesChartProps = Omit<ChartContainerProps, 'children'> & {
|
|
|
187
187
|
* «$1200» in the tooltip, and the axis is the one you read while comparing.
|
|
188
188
|
*/
|
|
189
189
|
yTickFormatter?: ((value: unknown) => string) | undefined;
|
|
190
|
+
/**
|
|
191
|
+
* The top of the value axis, when the scale has one that the data does not
|
|
192
|
+
* reach — 100 for a percentage.
|
|
193
|
+
*
|
|
194
|
+
* Without it the axis ends at the largest datum, and on a chart of «% visto»
|
|
195
|
+
* a course watched to 40 % draws as a full bar when it is the highest on the
|
|
196
|
+
* list. On `BarChart orientation="horizontal"` nothing gives that away: the
|
|
197
|
+
* value axis is hidden by design, so the chart says something false and
|
|
198
|
+
* looks exactly like one that does not.
|
|
199
|
+
*
|
|
200
|
+
* The bottom is always zero. A bar that does not start at zero is a
|
|
201
|
+
* different lie, and this prop is not a way into it. It is a floor for the
|
|
202
|
+
* top and not a clip either: a datum above it extends the axis instead of
|
|
203
|
+
* running off the edge. See `docs/decisions/0.11.md` § 58.
|
|
204
|
+
*/
|
|
205
|
+
valueMax?: number | undefined;
|
|
190
206
|
/**
|
|
191
207
|
* Shows the legend. It defaults to «only when there is more than one series»:
|
|
192
208
|
* a legend naming the one line already named by the chart's own heading is a
|
|
@@ -217,7 +233,7 @@ type AreaChartProps = SeriesChartProps & {
|
|
|
217
233
|
/** Adds the series up instead of overlaying them. */
|
|
218
234
|
stacked?: boolean | undefined;
|
|
219
235
|
};
|
|
220
|
-
declare function AreaChart({ data, series, xKey, formatter, xTickFormatter, yTickFormatter, legend, stacked, ...container }: AreaChartProps): react.JSX.Element;
|
|
236
|
+
declare function AreaChart({ data, series, xKey, formatter, xTickFormatter, yTickFormatter, valueMax, legend, stacked, ...container }: AreaChartProps): react.JSX.Element;
|
|
221
237
|
/**
|
|
222
238
|
* Bars, upright or lying down.
|
|
223
239
|
*
|
|
@@ -241,7 +257,7 @@ type BarChartProps = SeriesChartProps & {
|
|
|
241
257
|
/** Stacks the series instead of putting them side by side. */
|
|
242
258
|
stacked?: boolean | undefined;
|
|
243
259
|
};
|
|
244
|
-
declare function BarChart({ data, series, xKey, formatter, xTickFormatter, yTickFormatter, legend, orientation, stacked, ...container }: BarChartProps): react.JSX.Element;
|
|
260
|
+
declare function BarChart({ data, series, xKey, formatter, xTickFormatter, yTickFormatter, valueMax, legend, orientation, stacked, ...container }: BarChartProps): react.JSX.Element;
|
|
245
261
|
/**
|
|
246
262
|
* Lines, for comparing series against each other.
|
|
247
263
|
*
|
|
@@ -254,6 +270,6 @@ declare function BarChart({ data, series, xKey, formatter, xTickFormatter, yTick
|
|
|
254
270
|
* where it means «this one».
|
|
255
271
|
*/
|
|
256
272
|
type LineChartProps = SeriesChartProps;
|
|
257
|
-
declare function LineChart({ data, series, xKey, formatter, xTickFormatter, yTickFormatter, legend, ...container }: LineChartProps): react.JSX.Element;
|
|
273
|
+
declare function LineChart({ data, series, xKey, formatter, xTickFormatter, yTickFormatter, valueMax, legend, ...container }: LineChartProps): react.JSX.Element;
|
|
258
274
|
|
|
259
275
|
export { AreaChart, type AreaChartProps, BarChart, type BarChartProps, ChartContainer, type ChartContainerProps, type ChartDatum, ChartLegend, ChartLegendContent, type ChartLegendContentProps, type ChartPayloadItem, type ChartSeries, ChartTooltip, ChartTooltipContent, type ChartTooltipContentProps, LineChart, type LineChartProps, SERIES_COLORS, type SeriesChartProps, seriesColor };
|
package/dist/chart/index.d.ts
CHANGED
|
@@ -187,6 +187,22 @@ type SeriesChartProps = Omit<ChartContainerProps, 'children'> & {
|
|
|
187
187
|
* «$1200» in the tooltip, and the axis is the one you read while comparing.
|
|
188
188
|
*/
|
|
189
189
|
yTickFormatter?: ((value: unknown) => string) | undefined;
|
|
190
|
+
/**
|
|
191
|
+
* The top of the value axis, when the scale has one that the data does not
|
|
192
|
+
* reach — 100 for a percentage.
|
|
193
|
+
*
|
|
194
|
+
* Without it the axis ends at the largest datum, and on a chart of «% visto»
|
|
195
|
+
* a course watched to 40 % draws as a full bar when it is the highest on the
|
|
196
|
+
* list. On `BarChart orientation="horizontal"` nothing gives that away: the
|
|
197
|
+
* value axis is hidden by design, so the chart says something false and
|
|
198
|
+
* looks exactly like one that does not.
|
|
199
|
+
*
|
|
200
|
+
* The bottom is always zero. A bar that does not start at zero is a
|
|
201
|
+
* different lie, and this prop is not a way into it. It is a floor for the
|
|
202
|
+
* top and not a clip either: a datum above it extends the axis instead of
|
|
203
|
+
* running off the edge. See `docs/decisions/0.11.md` § 58.
|
|
204
|
+
*/
|
|
205
|
+
valueMax?: number | undefined;
|
|
190
206
|
/**
|
|
191
207
|
* Shows the legend. It defaults to «only when there is more than one series»:
|
|
192
208
|
* a legend naming the one line already named by the chart's own heading is a
|
|
@@ -217,7 +233,7 @@ type AreaChartProps = SeriesChartProps & {
|
|
|
217
233
|
/** Adds the series up instead of overlaying them. */
|
|
218
234
|
stacked?: boolean | undefined;
|
|
219
235
|
};
|
|
220
|
-
declare function AreaChart({ data, series, xKey, formatter, xTickFormatter, yTickFormatter, legend, stacked, ...container }: AreaChartProps): react.JSX.Element;
|
|
236
|
+
declare function AreaChart({ data, series, xKey, formatter, xTickFormatter, yTickFormatter, valueMax, legend, stacked, ...container }: AreaChartProps): react.JSX.Element;
|
|
221
237
|
/**
|
|
222
238
|
* Bars, upright or lying down.
|
|
223
239
|
*
|
|
@@ -241,7 +257,7 @@ type BarChartProps = SeriesChartProps & {
|
|
|
241
257
|
/** Stacks the series instead of putting them side by side. */
|
|
242
258
|
stacked?: boolean | undefined;
|
|
243
259
|
};
|
|
244
|
-
declare function BarChart({ data, series, xKey, formatter, xTickFormatter, yTickFormatter, legend, orientation, stacked, ...container }: BarChartProps): react.JSX.Element;
|
|
260
|
+
declare function BarChart({ data, series, xKey, formatter, xTickFormatter, yTickFormatter, valueMax, legend, orientation, stacked, ...container }: BarChartProps): react.JSX.Element;
|
|
245
261
|
/**
|
|
246
262
|
* Lines, for comparing series against each other.
|
|
247
263
|
*
|
|
@@ -254,6 +270,6 @@ declare function BarChart({ data, series, xKey, formatter, xTickFormatter, yTick
|
|
|
254
270
|
* where it means «this one».
|
|
255
271
|
*/
|
|
256
272
|
type LineChartProps = SeriesChartProps;
|
|
257
|
-
declare function LineChart({ data, series, xKey, formatter, xTickFormatter, yTickFormatter, legend, ...container }: LineChartProps): react.JSX.Element;
|
|
273
|
+
declare function LineChart({ data, series, xKey, formatter, xTickFormatter, yTickFormatter, valueMax, legend, ...container }: LineChartProps): react.JSX.Element;
|
|
258
274
|
|
|
259
275
|
export { AreaChart, type AreaChartProps, BarChart, type BarChartProps, ChartContainer, type ChartContainerProps, type ChartDatum, ChartLegend, ChartLegendContent, type ChartLegendContentProps, type ChartPayloadItem, type ChartSeries, ChartTooltip, ChartTooltipContent, type ChartTooltipContentProps, LineChart, type LineChartProps, SERIES_COLORS, type SeriesChartProps, seriesColor };
|
package/dist/chart/index.js
CHANGED
|
@@ -106,6 +106,7 @@ var color = (series, index) => series.color ?? seriesColor(index);
|
|
|
106
106
|
var MARGIN = { left: 0, right: 12, top: 8, bottom: 0 };
|
|
107
107
|
var AXIS = { tickLine: false, axisLine: false };
|
|
108
108
|
var tickOf = (format) => format ? { tickFormatter: format } : {};
|
|
109
|
+
var domainOf = (max) => max === void 0 ? {} : { domain: [0, max] };
|
|
109
110
|
var legendOf = (series, legend) => legend ?? series.length > 1 ? /* @__PURE__ */ jsx(ChartLegend, { content: /* @__PURE__ */ jsx(ChartLegendContent, {}) }, "legend") : null;
|
|
110
111
|
var tooltipOf = (formatter) => /* @__PURE__ */ jsx(
|
|
111
112
|
ChartTooltip,
|
|
@@ -122,6 +123,7 @@ function AreaChart({
|
|
|
122
123
|
formatter,
|
|
123
124
|
xTickFormatter,
|
|
124
125
|
yTickFormatter,
|
|
126
|
+
valueMax,
|
|
125
127
|
legend,
|
|
126
128
|
stacked = false,
|
|
127
129
|
...container
|
|
@@ -134,7 +136,7 @@ function AreaChart({
|
|
|
134
136
|
] }, s.key)) }),
|
|
135
137
|
/* @__PURE__ */ jsx(CartesianGrid, { vertical: false }),
|
|
136
138
|
/* @__PURE__ */ jsx(XAxis, { dataKey: xKey, ...AXIS, tickMargin: 8, ...tickOf(xTickFormatter) }),
|
|
137
|
-
/* @__PURE__ */ jsx(YAxis, { ...AXIS, width: 40, ...tickOf(yTickFormatter) }),
|
|
139
|
+
/* @__PURE__ */ jsx(YAxis, { ...AXIS, width: 40, ...tickOf(yTickFormatter), ...domainOf(valueMax) }),
|
|
138
140
|
tooltipOf(formatter),
|
|
139
141
|
legendOf(series, legend),
|
|
140
142
|
series.map((s, i) => /* @__PURE__ */ jsx(
|
|
@@ -160,6 +162,7 @@ function BarChart({
|
|
|
160
162
|
formatter,
|
|
161
163
|
xTickFormatter,
|
|
162
164
|
yTickFormatter,
|
|
165
|
+
valueMax,
|
|
163
166
|
legend,
|
|
164
167
|
orientation = "vertical",
|
|
165
168
|
stacked = false,
|
|
@@ -186,7 +189,16 @@ function BarChart({
|
|
|
186
189
|
},
|
|
187
190
|
"category"
|
|
188
191
|
),
|
|
189
|
-
/* @__PURE__ */ jsx(
|
|
192
|
+
/* @__PURE__ */ jsx(
|
|
193
|
+
XAxis,
|
|
194
|
+
{
|
|
195
|
+
type: "number",
|
|
196
|
+
hide: true,
|
|
197
|
+
...tickOf(yTickFormatter),
|
|
198
|
+
...domainOf(valueMax)
|
|
199
|
+
},
|
|
200
|
+
"value"
|
|
201
|
+
)
|
|
190
202
|
] : [
|
|
191
203
|
/* @__PURE__ */ jsx(
|
|
192
204
|
XAxis,
|
|
@@ -198,7 +210,16 @@ function BarChart({
|
|
|
198
210
|
},
|
|
199
211
|
"category"
|
|
200
212
|
),
|
|
201
|
-
/* @__PURE__ */ jsx(
|
|
213
|
+
/* @__PURE__ */ jsx(
|
|
214
|
+
YAxis,
|
|
215
|
+
{
|
|
216
|
+
...AXIS,
|
|
217
|
+
width: 40,
|
|
218
|
+
...tickOf(yTickFormatter),
|
|
219
|
+
...domainOf(valueMax)
|
|
220
|
+
},
|
|
221
|
+
"value"
|
|
222
|
+
)
|
|
202
223
|
],
|
|
203
224
|
tooltipOf(formatter),
|
|
204
225
|
legendOf(series, legend),
|
|
@@ -225,13 +246,14 @@ function LineChart({
|
|
|
225
246
|
formatter,
|
|
226
247
|
xTickFormatter,
|
|
227
248
|
yTickFormatter,
|
|
249
|
+
valueMax,
|
|
228
250
|
legend,
|
|
229
251
|
...container
|
|
230
252
|
}) {
|
|
231
253
|
return /* @__PURE__ */ jsx(ChartContainer, { ...container, children: /* @__PURE__ */ jsxs(LineChart$1, { data: rows(data), margin: MARGIN, children: [
|
|
232
254
|
/* @__PURE__ */ jsx(CartesianGrid, { vertical: false }),
|
|
233
255
|
/* @__PURE__ */ jsx(XAxis, { dataKey: xKey, ...AXIS, tickMargin: 8, ...tickOf(xTickFormatter) }),
|
|
234
|
-
/* @__PURE__ */ jsx(YAxis, { ...AXIS, width: 40, ...tickOf(yTickFormatter) }),
|
|
256
|
+
/* @__PURE__ */ jsx(YAxis, { ...AXIS, width: 40, ...tickOf(yTickFormatter), ...domainOf(valueMax) }),
|
|
235
257
|
tooltipOf(formatter),
|
|
236
258
|
legendOf(series, legend),
|
|
237
259
|
series.map((s, i) => /* @__PURE__ */ jsx(
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { cn } from './chunk-FAAGZG7A.js';
|
|
2
2
|
import { fins, ASSETS_PATH, poses, faces } from './chunk-CKRSQPTX.js';
|
|
3
3
|
import { naming, tagline } from './chunk-FGFNK72B.js';
|
|
4
|
-
import {
|
|
4
|
+
import { jsxs, Fragment, jsx } from 'react/jsx-runtime';
|
|
5
5
|
|
|
6
6
|
function Isotype({
|
|
7
7
|
background = "dark",
|
|
@@ -10,18 +10,24 @@ function Isotype({
|
|
|
10
10
|
className,
|
|
11
11
|
...props
|
|
12
12
|
}) {
|
|
13
|
-
const
|
|
14
|
-
return /* @__PURE__ */ jsx(
|
|
13
|
+
const fin = (file, mode) => /* @__PURE__ */ jsx(
|
|
15
14
|
"img",
|
|
16
15
|
{
|
|
17
16
|
src: `${basePath}/${file}`,
|
|
18
17
|
alt,
|
|
19
18
|
width: 147,
|
|
20
19
|
height: 111,
|
|
21
|
-
className: cn("h-8 w-auto select-none", className),
|
|
20
|
+
className: cn("h-8 w-auto select-none", className, mode),
|
|
22
21
|
...props
|
|
23
22
|
}
|
|
24
23
|
);
|
|
24
|
+
if (background === "auto") {
|
|
25
|
+
return /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
26
|
+
fin(fins.foam, "light:hidden"),
|
|
27
|
+
fin(fins.color, "not-light:hidden")
|
|
28
|
+
] });
|
|
29
|
+
}
|
|
30
|
+
return fin(background === "dark" ? fins.foam : fins.color);
|
|
25
31
|
}
|
|
26
32
|
function Logo({
|
|
27
33
|
background = "dark",
|
package/dist/index.cjs
CHANGED
|
@@ -2874,25 +2874,62 @@ function CourseCard({
|
|
|
2874
2874
|
meta,
|
|
2875
2875
|
status,
|
|
2876
2876
|
progress,
|
|
2877
|
+
media,
|
|
2878
|
+
footer,
|
|
2877
2879
|
className,
|
|
2878
2880
|
...props
|
|
2879
2881
|
}) {
|
|
2880
|
-
|
|
2881
|
-
|
|
2882
|
-
|
|
2883
|
-
|
|
2884
|
-
|
|
2885
|
-
|
|
2886
|
-
|
|
2887
|
-
|
|
2888
|
-
|
|
2889
|
-
|
|
2890
|
-
|
|
2891
|
-
|
|
2892
|
-
|
|
2893
|
-
|
|
2894
|
-
|
|
2895
|
-
|
|
2882
|
+
const hasProgress = typeof progress === "number";
|
|
2883
|
+
return (
|
|
2884
|
+
// `overflow-hidden` only with a cover: it is what rounds the cover's top
|
|
2885
|
+
// corners to the card's. It clips the content and not the `<a>` itself, so
|
|
2886
|
+
// the focus ring — an outline three pixels outside — still shows.
|
|
2887
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
2888
|
+
CardShell,
|
|
2889
|
+
{
|
|
2890
|
+
className: cn("p-step-lg", media ? "overflow-hidden" : null, className),
|
|
2891
|
+
...props,
|
|
2892
|
+
children: /* @__PURE__ */ jsxRuntime.jsxs("article", { className: "gap-step-sm flex h-full flex-col", children: [
|
|
2893
|
+
media ? (
|
|
2894
|
+
// The bleed is the card's own padding, negated. A call site that
|
|
2895
|
+
// changes the padding through `className` has to change this too, and
|
|
2896
|
+
// that is the trade for keeping the padding on the shell like every
|
|
2897
|
+
// other card instead of in a second place only when there is a cover.
|
|
2898
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "-mx-step-lg -mt-step-lg mb-step-xs", children: media })
|
|
2899
|
+
) : null,
|
|
2900
|
+
status ? /* @__PURE__ */ jsxRuntime.jsx("div", { children: /* @__PURE__ */ jsxRuntime.jsx(Badge, { variant: "warm", children: status }) }) : null,
|
|
2901
|
+
/* @__PURE__ */ jsxRuntime.jsx(Text, { as: "h3", variant: "h3", className: "group-hover:text-accent transition-standard", children: title }),
|
|
2902
|
+
summary ? /* @__PURE__ */ jsxRuntime.jsx(Text, { variant: "ui", tone: "secondary", className: "line-clamp-3", children: summary }) : null,
|
|
2903
|
+
meta && meta.length > 0 ? /* @__PURE__ */ jsxRuntime.jsx(Text, { variant: "label", tone: "muted", as: "p", className: "gap-step-xs flex flex-wrap items-center", children: meta.map((m, i) => /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "gap-step-xs flex items-center", children: [
|
|
2904
|
+
i > 0 ? /* @__PURE__ */ jsxRuntime.jsx("span", { "aria-hidden": "true", children: "\xB7" }) : null,
|
|
2905
|
+
m
|
|
2906
|
+
] }, i)) }) : null,
|
|
2907
|
+
hasProgress ? /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "gap-step-xs mt-auto flex flex-col pt-step-sm", children: [
|
|
2908
|
+
/* @__PURE__ */ jsxRuntime.jsx(Progress, { value: progress, tone: "warm", label: `Progreso del curso: ${progress}%` }),
|
|
2909
|
+
/* @__PURE__ */ jsxRuntime.jsxs(Text, { variant: "label", tone: "muted", as: "p", children: [
|
|
2910
|
+
progress,
|
|
2911
|
+
"% cursado"
|
|
2912
|
+
] })
|
|
2913
|
+
] }) : null,
|
|
2914
|
+
footer ? (
|
|
2915
|
+
// Only one of the two takes `mt-auto`. Two auto margins in one column
|
|
2916
|
+
// split the free space between them, and the progress bar would float
|
|
2917
|
+
// halfway down the card.
|
|
2918
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
2919
|
+
"div",
|
|
2920
|
+
{
|
|
2921
|
+
className: cn(
|
|
2922
|
+
"gap-step-sm pt-step-sm flex flex-wrap items-center justify-between",
|
|
2923
|
+
hasProgress ? null : "mt-auto"
|
|
2924
|
+
),
|
|
2925
|
+
children: footer
|
|
2926
|
+
}
|
|
2927
|
+
)
|
|
2928
|
+
) : null
|
|
2929
|
+
] })
|
|
2930
|
+
}
|
|
2931
|
+
)
|
|
2932
|
+
);
|
|
2896
2933
|
}
|
|
2897
2934
|
|
|
2898
2935
|
// src/brand/catalog.ts
|
|
@@ -3643,12 +3680,21 @@ function PageHeader({
|
|
|
3643
3680
|
action,
|
|
3644
3681
|
size: size2,
|
|
3645
3682
|
as = "h1",
|
|
3683
|
+
titleVariant,
|
|
3646
3684
|
className,
|
|
3647
3685
|
...props
|
|
3648
3686
|
}) {
|
|
3649
3687
|
return /* @__PURE__ */ jsxRuntime.jsxs("header", { className: cn(header({ size: size2 }), className), ...props, children: [
|
|
3650
3688
|
eyebrow ? /* @__PURE__ */ jsxRuntime.jsx(Text, { variant: "eyebrow", tone: "accent", as: "p", children: eyebrow }) : null,
|
|
3651
|
-
/* @__PURE__ */ jsxRuntime.jsx(
|
|
3689
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
3690
|
+
Text,
|
|
3691
|
+
{
|
|
3692
|
+
as,
|
|
3693
|
+
variant: titleVariant ?? (size2 === "display" ? "display" : "h1"),
|
|
3694
|
+
className: "max-w-measure",
|
|
3695
|
+
children: title
|
|
3696
|
+
}
|
|
3697
|
+
),
|
|
3652
3698
|
description ? /* @__PURE__ */ jsxRuntime.jsx(Text, { variant: "body", tone: "secondary", children: description }) : null,
|
|
3653
3699
|
action ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "gap-step-sm mt-step-sm flex flex-wrap items-center", children: action }) : null
|
|
3654
3700
|
] });
|
|
@@ -3895,18 +3941,24 @@ function Isotype({
|
|
|
3895
3941
|
className,
|
|
3896
3942
|
...props
|
|
3897
3943
|
}) {
|
|
3898
|
-
const
|
|
3899
|
-
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
3944
|
+
const fin = (file, mode) => /* @__PURE__ */ jsxRuntime.jsx(
|
|
3900
3945
|
"img",
|
|
3901
3946
|
{
|
|
3902
3947
|
src: `${basePath}/${file}`,
|
|
3903
3948
|
alt,
|
|
3904
3949
|
width: 147,
|
|
3905
3950
|
height: 111,
|
|
3906
|
-
className: cn("h-8 w-auto select-none", className),
|
|
3951
|
+
className: cn("h-8 w-auto select-none", className, mode),
|
|
3907
3952
|
...props
|
|
3908
3953
|
}
|
|
3909
3954
|
);
|
|
3955
|
+
if (background === "auto") {
|
|
3956
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
|
|
3957
|
+
fin(fins.foam, "light:hidden"),
|
|
3958
|
+
fin(fins.color, "not-light:hidden")
|
|
3959
|
+
] });
|
|
3960
|
+
}
|
|
3961
|
+
return fin(background === "dark" ? fins.foam : fins.color);
|
|
3910
3962
|
}
|
|
3911
3963
|
function Logo({
|
|
3912
3964
|
background = "dark",
|
package/dist/index.d.cts
CHANGED
|
@@ -26,7 +26,7 @@ import * as ToastPrimitive from '@radix-ui/react-toast';
|
|
|
26
26
|
import * as TooltipPrimitive from '@radix-ui/react-tooltip';
|
|
27
27
|
import { F as Face, P as Pose } from './catalog-D13txprv.cjs';
|
|
28
28
|
export { A as ASSETS_PATH, B as Background, a as Fin, f as faceList, b as faceUsage, c as faces, d as fins, p as poseList, e as poses } from './catalog-D13txprv.cjs';
|
|
29
|
-
export { Isotype, IsotypeProps, Logo, LogoProps, Mascot, MascotFace, MascotFaceProps, MascotProps } from './brand/index.cjs';
|
|
29
|
+
export { Isotype, IsotypeBackground, IsotypeProps, Logo, LogoProps, Mascot, MascotFace, MascotFaceProps, MascotProps } from './brand/index.cjs';
|
|
30
30
|
import { ClassValue } from 'clsx';
|
|
31
31
|
import '@radix-ui/react-label';
|
|
32
32
|
|
|
@@ -869,7 +869,13 @@ type CodeBlockProps = Omit<ComponentPropsWithoutRef<'div'>, 'children'> & {
|
|
|
869
869
|
};
|
|
870
870
|
declare function CodeBlock({ children, language, copyText, className, ...props }: CodeBlockProps): react.JSX.Element;
|
|
871
871
|
|
|
872
|
-
|
|
872
|
+
/**
|
|
873
|
+
* `media` is left out of the shell's props because `<a>` already has an
|
|
874
|
+
* attribute by that name — a media query for the linked resource, a string —
|
|
875
|
+
* and intersected with the slot it would make the slot accept only strings.
|
|
876
|
+
* No browser acts on it and no project was passing it.
|
|
877
|
+
*/
|
|
878
|
+
type CourseCardProps = Omit<CardShellProps, 'children' | 'title' | 'media'> & {
|
|
873
879
|
title: ReactNode;
|
|
874
880
|
summary?: ReactNode;
|
|
875
881
|
/** Level, duration, number of lessons: whatever the project wants to list. */
|
|
@@ -881,8 +887,42 @@ type CourseCardProps = Omit<CardShellProps, 'children' | 'title'> & {
|
|
|
881
887
|
* when passed, the bar goes in sand, which is the color of course progress.
|
|
882
888
|
*/
|
|
883
889
|
progress?: number | undefined;
|
|
890
|
+
/**
|
|
891
|
+
* The cover, at the top and bleeding to the card's edges, with `alt=""`: the
|
|
892
|
+
* whole card is one link and `title` already names it.
|
|
893
|
+
*
|
|
894
|
+
* The project owns the element — an `<img>`, a framework's `Image`, a
|
|
895
|
+
* generated cover — and its ratio; the card clips it to its own corners. An
|
|
896
|
+
* `alt` that repeats the title makes a screen reader say the course twice
|
|
897
|
+
* before anything else.
|
|
898
|
+
*/
|
|
899
|
+
media?: ReactNode;
|
|
900
|
+
/**
|
|
901
|
+
* The closing row, under `meta`: the rating, the price, whatever the project
|
|
902
|
+
* sells the course with. It sits at the bottom of the card, so the rows of a
|
|
903
|
+
* grid line up whatever the length of each summary.
|
|
904
|
+
*/
|
|
905
|
+
footer?: ReactNode;
|
|
884
906
|
};
|
|
885
|
-
|
|
907
|
+
/**
|
|
908
|
+
* The course, as a card that links to it.
|
|
909
|
+
*
|
|
910
|
+
* `media` and `footer` are slots rather than props for a cover URL, a rating
|
|
911
|
+
* and a price, because none of those three is the identity's. The price comes
|
|
912
|
+
* formatted in a currency the library does not know, the rating is drawn by a
|
|
913
|
+
* component the project already has, and the cover is an image pipeline. What
|
|
914
|
+
* IS the identity's stays here: the title's scale and its hover, the sand bar,
|
|
915
|
+
* the status badge. See `docs/decisions/0.11.md` § 59.
|
|
916
|
+
*
|
|
917
|
+
* The title does NOT go over the cover. It stays in the body at `h3`, with the
|
|
918
|
+
* hover every other card has. Text on a photograph needs a scrim, and a scrim's
|
|
919
|
+
* contrast depends on the photograph: it cannot be measured once and recorded,
|
|
920
|
+
* which is the only way contrast is decided in this system.
|
|
921
|
+
*
|
|
922
|
+
* And the cover does not move on hover. Rule 6 is the border and nothing else:
|
|
923
|
+
* no zoom, no scale, no displacement.
|
|
924
|
+
*/
|
|
925
|
+
declare function CourseCard({ title, summary, meta, status, progress, media, footer, className, ...props }: CourseCardProps): react.JSX.Element;
|
|
886
926
|
|
|
887
927
|
/**
|
|
888
928
|
* The mascot's most important rule, finally as code.
|
|
@@ -1392,6 +1432,15 @@ declare function NewsletterForm({ title, description, state, onSubmitEmail, succ
|
|
|
1392
1432
|
*
|
|
1393
1433
|
* `display` for covers, `page` for section headers.
|
|
1394
1434
|
*
|
|
1435
|
+
* The size picks the title's scale by default, and `titleVariant` lets the
|
|
1436
|
+
* screen pick another one. The default is the document's — «h1 44/700» on the
|
|
1437
|
+
* six interior pages of the reading site — and it is right there. It is not
|
|
1438
|
+
* right in the two admin apps: `blog-content-manager` titles its twelve screens
|
|
1439
|
+
* at 24px, and `cursos` titles 29 of its 32 at `h3` — every one in the panel —
|
|
1440
|
+
* and the other three, the public catalog pages, at `h2`. Both are rungs the
|
|
1441
|
+
* scale already has, and a third `size` could only have named one of them. See
|
|
1442
|
+
* `docs/decisions/0.11.md` § 57.
|
|
1443
|
+
*
|
|
1395
1444
|
* It takes no mascot face, at either scale: faces go in empty states,
|
|
1396
1445
|
* confirmations, errors, course progress and celebration.
|
|
1397
1446
|
*
|
|
@@ -1416,8 +1465,21 @@ type PageHeaderProps = Omit<ComponentPropsWithoutRef<'header'>, 'title'> & Varia
|
|
|
1416
1465
|
action?: ReactNode | undefined;
|
|
1417
1466
|
/** The headline's level. `h1` unless the page already has one. */
|
|
1418
1467
|
as?: 'h1' | 'h2' | undefined;
|
|
1468
|
+
/**
|
|
1469
|
+
* The headline's scale, when the screen needs a different one from what
|
|
1470
|
+
* `size` gives — `display` for `display`, `h1` for `page`.
|
|
1471
|
+
*
|
|
1472
|
+
* It is the same split `Text` makes: `as` is the level, this is how big it
|
|
1473
|
+
* looks. A panel's `<h1>` at `h3` is still the page's only `h1`. The padding
|
|
1474
|
+
* stays with `size`, so a header inside a layout that already spaces its
|
|
1475
|
+
* content passes `className="py-0"`.
|
|
1476
|
+
*
|
|
1477
|
+
* Only the four headline scales, all in the display family. `stat` is for
|
|
1478
|
+
* numbers and `body` is not a headline.
|
|
1479
|
+
*/
|
|
1480
|
+
titleVariant?: 'display' | 'h1' | 'h2' | 'h3' | undefined;
|
|
1419
1481
|
};
|
|
1420
|
-
declare function PageHeader({ title, eyebrow, description, action, size, as, className, ...props }: PageHeaderProps): react.JSX.Element;
|
|
1482
|
+
declare function PageHeader({ title, eyebrow, description, action, size, as, titleVariant, className, ...props }: PageHeaderProps): react.JSX.Element;
|
|
1421
1483
|
|
|
1422
1484
|
/**
|
|
1423
1485
|
* How much you have read. It is NOT `Progress` under another name.
|
package/dist/index.d.ts
CHANGED
|
@@ -26,7 +26,7 @@ import * as ToastPrimitive from '@radix-ui/react-toast';
|
|
|
26
26
|
import * as TooltipPrimitive from '@radix-ui/react-tooltip';
|
|
27
27
|
import { F as Face, P as Pose } from './catalog-D13txprv.js';
|
|
28
28
|
export { A as ASSETS_PATH, B as Background, a as Fin, f as faceList, b as faceUsage, c as faces, d as fins, p as poseList, e as poses } from './catalog-D13txprv.js';
|
|
29
|
-
export { Isotype, IsotypeProps, Logo, LogoProps, Mascot, MascotFace, MascotFaceProps, MascotProps } from './brand/index.js';
|
|
29
|
+
export { Isotype, IsotypeBackground, IsotypeProps, Logo, LogoProps, Mascot, MascotFace, MascotFaceProps, MascotProps } from './brand/index.js';
|
|
30
30
|
import { ClassValue } from 'clsx';
|
|
31
31
|
import '@radix-ui/react-label';
|
|
32
32
|
|
|
@@ -869,7 +869,13 @@ type CodeBlockProps = Omit<ComponentPropsWithoutRef<'div'>, 'children'> & {
|
|
|
869
869
|
};
|
|
870
870
|
declare function CodeBlock({ children, language, copyText, className, ...props }: CodeBlockProps): react.JSX.Element;
|
|
871
871
|
|
|
872
|
-
|
|
872
|
+
/**
|
|
873
|
+
* `media` is left out of the shell's props because `<a>` already has an
|
|
874
|
+
* attribute by that name — a media query for the linked resource, a string —
|
|
875
|
+
* and intersected with the slot it would make the slot accept only strings.
|
|
876
|
+
* No browser acts on it and no project was passing it.
|
|
877
|
+
*/
|
|
878
|
+
type CourseCardProps = Omit<CardShellProps, 'children' | 'title' | 'media'> & {
|
|
873
879
|
title: ReactNode;
|
|
874
880
|
summary?: ReactNode;
|
|
875
881
|
/** Level, duration, number of lessons: whatever the project wants to list. */
|
|
@@ -881,8 +887,42 @@ type CourseCardProps = Omit<CardShellProps, 'children' | 'title'> & {
|
|
|
881
887
|
* when passed, the bar goes in sand, which is the color of course progress.
|
|
882
888
|
*/
|
|
883
889
|
progress?: number | undefined;
|
|
890
|
+
/**
|
|
891
|
+
* The cover, at the top and bleeding to the card's edges, with `alt=""`: the
|
|
892
|
+
* whole card is one link and `title` already names it.
|
|
893
|
+
*
|
|
894
|
+
* The project owns the element — an `<img>`, a framework's `Image`, a
|
|
895
|
+
* generated cover — and its ratio; the card clips it to its own corners. An
|
|
896
|
+
* `alt` that repeats the title makes a screen reader say the course twice
|
|
897
|
+
* before anything else.
|
|
898
|
+
*/
|
|
899
|
+
media?: ReactNode;
|
|
900
|
+
/**
|
|
901
|
+
* The closing row, under `meta`: the rating, the price, whatever the project
|
|
902
|
+
* sells the course with. It sits at the bottom of the card, so the rows of a
|
|
903
|
+
* grid line up whatever the length of each summary.
|
|
904
|
+
*/
|
|
905
|
+
footer?: ReactNode;
|
|
884
906
|
};
|
|
885
|
-
|
|
907
|
+
/**
|
|
908
|
+
* The course, as a card that links to it.
|
|
909
|
+
*
|
|
910
|
+
* `media` and `footer` are slots rather than props for a cover URL, a rating
|
|
911
|
+
* and a price, because none of those three is the identity's. The price comes
|
|
912
|
+
* formatted in a currency the library does not know, the rating is drawn by a
|
|
913
|
+
* component the project already has, and the cover is an image pipeline. What
|
|
914
|
+
* IS the identity's stays here: the title's scale and its hover, the sand bar,
|
|
915
|
+
* the status badge. See `docs/decisions/0.11.md` § 59.
|
|
916
|
+
*
|
|
917
|
+
* The title does NOT go over the cover. It stays in the body at `h3`, with the
|
|
918
|
+
* hover every other card has. Text on a photograph needs a scrim, and a scrim's
|
|
919
|
+
* contrast depends on the photograph: it cannot be measured once and recorded,
|
|
920
|
+
* which is the only way contrast is decided in this system.
|
|
921
|
+
*
|
|
922
|
+
* And the cover does not move on hover. Rule 6 is the border and nothing else:
|
|
923
|
+
* no zoom, no scale, no displacement.
|
|
924
|
+
*/
|
|
925
|
+
declare function CourseCard({ title, summary, meta, status, progress, media, footer, className, ...props }: CourseCardProps): react.JSX.Element;
|
|
886
926
|
|
|
887
927
|
/**
|
|
888
928
|
* The mascot's most important rule, finally as code.
|
|
@@ -1392,6 +1432,15 @@ declare function NewsletterForm({ title, description, state, onSubmitEmail, succ
|
|
|
1392
1432
|
*
|
|
1393
1433
|
* `display` for covers, `page` for section headers.
|
|
1394
1434
|
*
|
|
1435
|
+
* The size picks the title's scale by default, and `titleVariant` lets the
|
|
1436
|
+
* screen pick another one. The default is the document's — «h1 44/700» on the
|
|
1437
|
+
* six interior pages of the reading site — and it is right there. It is not
|
|
1438
|
+
* right in the two admin apps: `blog-content-manager` titles its twelve screens
|
|
1439
|
+
* at 24px, and `cursos` titles 29 of its 32 at `h3` — every one in the panel —
|
|
1440
|
+
* and the other three, the public catalog pages, at `h2`. Both are rungs the
|
|
1441
|
+
* scale already has, and a third `size` could only have named one of them. See
|
|
1442
|
+
* `docs/decisions/0.11.md` § 57.
|
|
1443
|
+
*
|
|
1395
1444
|
* It takes no mascot face, at either scale: faces go in empty states,
|
|
1396
1445
|
* confirmations, errors, course progress and celebration.
|
|
1397
1446
|
*
|
|
@@ -1416,8 +1465,21 @@ type PageHeaderProps = Omit<ComponentPropsWithoutRef<'header'>, 'title'> & Varia
|
|
|
1416
1465
|
action?: ReactNode | undefined;
|
|
1417
1466
|
/** The headline's level. `h1` unless the page already has one. */
|
|
1418
1467
|
as?: 'h1' | 'h2' | undefined;
|
|
1468
|
+
/**
|
|
1469
|
+
* The headline's scale, when the screen needs a different one from what
|
|
1470
|
+
* `size` gives — `display` for `display`, `h1` for `page`.
|
|
1471
|
+
*
|
|
1472
|
+
* It is the same split `Text` makes: `as` is the level, this is how big it
|
|
1473
|
+
* looks. A panel's `<h1>` at `h3` is still the page's only `h1`. The padding
|
|
1474
|
+
* stays with `size`, so a header inside a layout that already spaces its
|
|
1475
|
+
* content passes `className="py-0"`.
|
|
1476
|
+
*
|
|
1477
|
+
* Only the four headline scales, all in the display family. `stat` is for
|
|
1478
|
+
* numbers and `body` is not a headline.
|
|
1479
|
+
*/
|
|
1480
|
+
titleVariant?: 'display' | 'h1' | 'h2' | 'h3' | undefined;
|
|
1419
1481
|
};
|
|
1420
|
-
declare function PageHeader({ title, eyebrow, description, action, size, as, className, ...props }: PageHeaderProps): react.JSX.Element;
|
|
1482
|
+
declare function PageHeader({ title, eyebrow, description, action, size, as, titleVariant, className, ...props }: PageHeaderProps): react.JSX.Element;
|
|
1421
1483
|
|
|
1422
1484
|
/**
|
|
1423
1485
|
* How much you have read. It is NOT `Progress` under another name.
|
package/dist/index.js
CHANGED
|
@@ -9,8 +9,8 @@ export { THEME_ATTRIBUTE, THEME_EVENT, THEME_KEY, applyTheme, currentTheme, pref
|
|
|
9
9
|
import { alert, button, avatar, badge, category, metric, CARD_SURFACE, CARD } from './chunk-XXDATT3A.js';
|
|
10
10
|
export { CARD, CARD_HOVER, CARD_SURFACE, alert as alertVariants, avatar as avatarVariants, badge as badgeVariants, button as buttonVariants, category as categoryBadgeVariants, metric as metricBadgeVariants } from './chunk-XXDATT3A.js';
|
|
11
11
|
export { text as textVariants } from './chunk-ODBFN44D.js';
|
|
12
|
-
import { MascotFace, Mascot } from './chunk-
|
|
13
|
-
export { Isotype, Logo, Mascot, MascotFace } from './chunk-
|
|
12
|
+
import { MascotFace, Mascot } from './chunk-RKGKO2TW.js';
|
|
13
|
+
export { Isotype, Logo, Mascot, MascotFace } from './chunk-RKGKO2TW.js';
|
|
14
14
|
import { Icon } from './chunk-ZSCSKCTY.js';
|
|
15
15
|
import { cn } from './chunk-FAAGZG7A.js';
|
|
16
16
|
export { cn } from './chunk-FAAGZG7A.js';
|
|
@@ -2181,25 +2181,62 @@ function CourseCard({
|
|
|
2181
2181
|
meta,
|
|
2182
2182
|
status,
|
|
2183
2183
|
progress,
|
|
2184
|
+
media,
|
|
2185
|
+
footer,
|
|
2184
2186
|
className,
|
|
2185
2187
|
...props
|
|
2186
2188
|
}) {
|
|
2187
|
-
|
|
2188
|
-
|
|
2189
|
-
|
|
2190
|
-
|
|
2191
|
-
|
|
2192
|
-
|
|
2193
|
-
|
|
2194
|
-
|
|
2195
|
-
|
|
2196
|
-
|
|
2197
|
-
|
|
2198
|
-
|
|
2199
|
-
|
|
2200
|
-
|
|
2201
|
-
|
|
2202
|
-
|
|
2189
|
+
const hasProgress = typeof progress === "number";
|
|
2190
|
+
return (
|
|
2191
|
+
// `overflow-hidden` only with a cover: it is what rounds the cover's top
|
|
2192
|
+
// corners to the card's. It clips the content and not the `<a>` itself, so
|
|
2193
|
+
// the focus ring — an outline three pixels outside — still shows.
|
|
2194
|
+
/* @__PURE__ */ jsx(
|
|
2195
|
+
CardShell,
|
|
2196
|
+
{
|
|
2197
|
+
className: cn("p-step-lg", media ? "overflow-hidden" : null, className),
|
|
2198
|
+
...props,
|
|
2199
|
+
children: /* @__PURE__ */ jsxs("article", { className: "gap-step-sm flex h-full flex-col", children: [
|
|
2200
|
+
media ? (
|
|
2201
|
+
// The bleed is the card's own padding, negated. A call site that
|
|
2202
|
+
// changes the padding through `className` has to change this too, and
|
|
2203
|
+
// that is the trade for keeping the padding on the shell like every
|
|
2204
|
+
// other card instead of in a second place only when there is a cover.
|
|
2205
|
+
/* @__PURE__ */ jsx("div", { className: "-mx-step-lg -mt-step-lg mb-step-xs", children: media })
|
|
2206
|
+
) : null,
|
|
2207
|
+
status ? /* @__PURE__ */ jsx("div", { children: /* @__PURE__ */ jsx(Badge, { variant: "warm", children: status }) }) : null,
|
|
2208
|
+
/* @__PURE__ */ jsx(Text, { as: "h3", variant: "h3", className: "group-hover:text-accent transition-standard", children: title }),
|
|
2209
|
+
summary ? /* @__PURE__ */ jsx(Text, { variant: "ui", tone: "secondary", className: "line-clamp-3", children: summary }) : null,
|
|
2210
|
+
meta && meta.length > 0 ? /* @__PURE__ */ jsx(Text, { variant: "label", tone: "muted", as: "p", className: "gap-step-xs flex flex-wrap items-center", children: meta.map((m, i) => /* @__PURE__ */ jsxs("span", { className: "gap-step-xs flex items-center", children: [
|
|
2211
|
+
i > 0 ? /* @__PURE__ */ jsx("span", { "aria-hidden": "true", children: "\xB7" }) : null,
|
|
2212
|
+
m
|
|
2213
|
+
] }, i)) }) : null,
|
|
2214
|
+
hasProgress ? /* @__PURE__ */ jsxs("div", { className: "gap-step-xs mt-auto flex flex-col pt-step-sm", children: [
|
|
2215
|
+
/* @__PURE__ */ jsx(Progress, { value: progress, tone: "warm", label: `Progreso del curso: ${progress}%` }),
|
|
2216
|
+
/* @__PURE__ */ jsxs(Text, { variant: "label", tone: "muted", as: "p", children: [
|
|
2217
|
+
progress,
|
|
2218
|
+
"% cursado"
|
|
2219
|
+
] })
|
|
2220
|
+
] }) : null,
|
|
2221
|
+
footer ? (
|
|
2222
|
+
// Only one of the two takes `mt-auto`. Two auto margins in one column
|
|
2223
|
+
// split the free space between them, and the progress bar would float
|
|
2224
|
+
// halfway down the card.
|
|
2225
|
+
/* @__PURE__ */ jsx(
|
|
2226
|
+
"div",
|
|
2227
|
+
{
|
|
2228
|
+
className: cn(
|
|
2229
|
+
"gap-step-sm pt-step-sm flex flex-wrap items-center justify-between",
|
|
2230
|
+
hasProgress ? null : "mt-auto"
|
|
2231
|
+
),
|
|
2232
|
+
children: footer
|
|
2233
|
+
}
|
|
2234
|
+
)
|
|
2235
|
+
) : null
|
|
2236
|
+
] })
|
|
2237
|
+
}
|
|
2238
|
+
)
|
|
2239
|
+
);
|
|
2203
2240
|
}
|
|
2204
2241
|
var FACE = "w-[66px] max-w-none";
|
|
2205
2242
|
function EmptyState(props) {
|
|
@@ -2881,12 +2918,21 @@ function PageHeader({
|
|
|
2881
2918
|
action,
|
|
2882
2919
|
size: size2,
|
|
2883
2920
|
as = "h1",
|
|
2921
|
+
titleVariant,
|
|
2884
2922
|
className,
|
|
2885
2923
|
...props
|
|
2886
2924
|
}) {
|
|
2887
2925
|
return /* @__PURE__ */ jsxs("header", { className: cn(header({ size: size2 }), className), ...props, children: [
|
|
2888
2926
|
eyebrow ? /* @__PURE__ */ jsx(Text, { variant: "eyebrow", tone: "accent", as: "p", children: eyebrow }) : null,
|
|
2889
|
-
/* @__PURE__ */ jsx(
|
|
2927
|
+
/* @__PURE__ */ jsx(
|
|
2928
|
+
Text,
|
|
2929
|
+
{
|
|
2930
|
+
as,
|
|
2931
|
+
variant: titleVariant ?? (size2 === "display" ? "display" : "h1"),
|
|
2932
|
+
className: "max-w-measure",
|
|
2933
|
+
children: title
|
|
2934
|
+
}
|
|
2935
|
+
),
|
|
2890
2936
|
description ? /* @__PURE__ */ jsx(Text, { variant: "body", tone: "secondary", children: description }) : null,
|
|
2891
2937
|
action ? /* @__PURE__ */ jsx("div", { className: "gap-step-sm mt-step-sm flex flex-wrap items-center", children: action }) : null
|
|
2892
2938
|
] });
|
package/llms.txt
CHANGED
|
@@ -480,6 +480,13 @@ re-exported, because they are unchanged and wrapping them buys nothing.
|
|
|
480
480
|
down for a ranking. Recharts calls that same thing `layout="vertical"` — if you
|
|
481
481
|
are porting code, the value flips.
|
|
482
482
|
|
|
483
|
+
**A percentage passes `valueMax={100}`.** Without it the value axis ends at the
|
|
484
|
+
largest datum, and on a horizontal ranking — whose value axis is hidden — a
|
|
485
|
+
course watched to 40 % draws as a full bar when it is the highest on the list.
|
|
486
|
+
The bottom is always zero, and it is a floor rather than a clip: a datum above
|
|
487
|
+
`valueMax` widens the axis instead of running off the edge. It is on all three
|
|
488
|
+
types. See `decisions/0.11.md` § 58.
|
|
489
|
+
|
|
483
490
|
`stacked` on `AreaChart` and `BarChart` adds the series up. Without it areas
|
|
484
491
|
overlap, which is honest and rarely what you want with more than one series: to
|
|
485
492
|
COMPARE rather than add up, the type is `LineChart`.
|
|
@@ -590,6 +597,10 @@ compiles and looks wrong, or that fails the project's accessibility audit.
|
|
|
590
597
|
the only member of the second criterion § 23 opened for it.
|
|
591
598
|
6. **Semantics and scale are independent.** An `h2` that has to look small is
|
|
592
599
|
`<Text as="h2" variant="h3">`, never an `h3` that lies about the hierarchy.
|
|
600
|
+
`PageHeader` makes the same split: `as` is the level and `titleVariant` the
|
|
601
|
+
scale. An admin panel's title is `<PageHeader title="Ventas"
|
|
602
|
+
titleVariant="h3" />` — still the page's only `h1`, at 25px instead of 44. See
|
|
603
|
+
`decisions/0.11.md` § 57.
|
|
593
604
|
7. **`textMuted` never goes over `surfaceRaised`**: it gives 4.07 in dark. Over a
|
|
594
605
|
raised surface — menus, active tabs — the token is `textSecondary`.
|
|
595
606
|
8. **A background tinted with a semantic color carries text from a text token**,
|
|
@@ -610,6 +621,11 @@ compiles and looks wrong, or that fails the project's accessibility audit.
|
|
|
610
621
|
not the humour contract. See `decisions/0.7.md` § 27.
|
|
611
622
|
12. **The fin is not a free parameter**: `foam` on a dark background, `color` on a
|
|
612
623
|
light one. The components already choose it from the background.
|
|
624
|
+
**On a site that switches theme, pass `background="auto"`** to `Isotype` or
|
|
625
|
+
`Logo`: both fins are rendered and the `light:` variant shows the one that
|
|
626
|
+
reads, so no call site has to know the theme. A surface that keeps one mode
|
|
627
|
+
whatever the page does — a dark panel on a light page — is a fixed
|
|
628
|
+
background, and it still says `dark`. See `decisions/0.11.md` § 60.
|
|
613
629
|
|
|
614
630
|
## What the library does NOT do, on purpose
|
|
615
631
|
|
|
@@ -625,6 +641,12 @@ These are the confusions people run into most often when consuming it.
|
|
|
625
641
|
provider.
|
|
626
642
|
- **It ships no router.** The components with links accept `asChild` to wrap the
|
|
627
643
|
framework's `Link`.
|
|
644
|
+
- **It does not know your prices, ratings or images.** `CourseCard` takes
|
|
645
|
+
`media` — the cover, bleeding to the edges, with `alt=""` because the title
|
|
646
|
+
already names the card — and `footer`, the closing row where a project puts the
|
|
647
|
+
rating and the price. Both are nodes the project draws; the card keeps the
|
|
648
|
+
title, its hover and the sand progress bar. The title does not go over the
|
|
649
|
+
cover. See `decisions/0.11.md` § 59.
|
|
628
650
|
- **It ships no `data-testid`.** A composed part your test suite has to reach is
|
|
629
651
|
reached with a slot: `ArticleCard`'s `tagAsChild`, `Breadcrumb`'s and
|
|
630
652
|
`TableOfContents`'s `linkAsChild`. They hand you the element and its
|
|
@@ -1389,11 +1411,15 @@ Source: `src/components/code-block/index.tsx`
|
|
|
1389
1411
|
|
|
1390
1412
|
Source: `src/components/course-card/index.tsx`
|
|
1391
1413
|
|
|
1392
|
-
|
|
1414
|
+
The course, as a card that links to it.
|
|
1415
|
+
|
|
1416
|
+
- Extends: `Omit<CardShellProps, 'children' \| 'title' \| 'media'>`
|
|
1393
1417
|
|
|
1394
1418
|
| prop | type | req. | default | what it does |
|
|
1395
1419
|
| --- | --- | --- | --- | --- |
|
|
1396
1420
|
| `asChild` | `boolean \| undefined` | | | Renders the child instead of an `<a>`. It is how Next's or Astro's `Link` plugs in without the library depending on any router. |
|
|
1421
|
+
| `footer` | `ReactNode` | | | The closing row, under `meta`: the rating, the price, whatever the project sells the course with. It sits at the bottom of the card, so the rows of a grid line up whatever the length of each summary. |
|
|
1422
|
+
| `media` | `ReactNode` | | | The cover, at the top and bleeding to the card's edges, with `alt=""`: the whole card is one link and `title` already names it. |
|
|
1397
1423
|
| `meta` | `readonly ReactNode[]` | | | Level, duration, number of lessons: whatever the project wants to list. |
|
|
1398
1424
|
| `progress` | `number` | | | Percentage completed. It only makes sense for someone already enrolled; when passed, the bar goes in sand, which is the color of course progress. |
|
|
1399
1425
|
| `status` | `ReactNode` | | | Status label: «próximamente», «gratis», «nuevo». |
|
|
@@ -1557,6 +1583,7 @@ One header at two scales, not two components.
|
|
|
1557
1583
|
| `eyebrow` | `ReactNode` | | | Mono, small caps, in accent. It is the section the page belongs to. |
|
|
1558
1584
|
| `size` | `"display" \| "page"` | | `page` | |
|
|
1559
1585
|
| `title` | `ReactNode` | yes | | |
|
|
1586
|
+
| `titleVariant` | `"display" \| "h1" \| "h2" \| "h3"` | | | The headline's scale, when the screen needs a different one from what `size` gives — `display` for `display`, `h1` for `page`. |
|
|
1560
1587
|
|
|
1561
1588
|
### ScrollingProgressBar
|
|
1562
1589
|
|
|
@@ -1645,12 +1672,14 @@ Imported from `@eduardoalvarez/arrecife` or `@eduardoalvarez/arrecife/brand`. 4
|
|
|
1645
1672
|
|
|
1646
1673
|
Source: `src/brand/isotype.tsx`
|
|
1647
1674
|
|
|
1675
|
+
The fin, in the variant its background asks for.
|
|
1676
|
+
|
|
1648
1677
|
- Extends: `Omit<ComponentPropsWithoutRef<'img'>, 'src' \| 'alt'>`
|
|
1649
1678
|
|
|
1650
1679
|
| prop | type | req. | default | what it does |
|
|
1651
1680
|
| --- | --- | --- | --- | --- |
|
|
1652
1681
|
| `alt` | `string` | | | Alt text. Empty when the isotype accompanies text that already names it. |
|
|
1653
|
-
| `background` | `"dark" \| "light"` | | `dark` | Which background it sits on. Deciding is mandatory even though it has a default: the fin's body is nearly black, so the two-blue variant disappears over abyss.
|
|
1682
|
+
| `background` | `"dark" \| "light" \| "auto"` | | `dark` | Which background it sits on, or `auto` on a site that switches theme: both fins are rendered and CSS shows the one that reads. Deciding is mandatory even though it has a default: the fin's body is nearly black, so the two-blue variant disappears over abyss. |
|
|
1654
1683
|
| `basePath` | `string` | | `ASSETS_PATH` | |
|
|
1655
1684
|
|
|
1656
1685
|
### Logo
|
|
@@ -1663,7 +1692,7 @@ The wordmark comes from `naming.wordmark`, not from a hand-written string, and i
|
|
|
1663
1692
|
|
|
1664
1693
|
| prop | type | req. | default | what it does |
|
|
1665
1694
|
| --- | --- | --- | --- | --- |
|
|
1666
|
-
| `background` | `"dark" \| "light"` | | `dark` |
|
|
1695
|
+
| `background` | `"dark" \| "light" \| "auto"` | | `dark` | The background the logo sits on, handed to its fin. `auto` follows the theme — see `Isotype`. The wordmark needs no help: it is `textPrimary`, which already follows the mode. |
|
|
1667
1696
|
| `basePath` | `string` | | `ASSETS_PATH` | |
|
|
1668
1697
|
| `isotypeOnly` | `boolean \| undefined` | | `false` | Hides the wordmark and leaves only the fin, for very narrow bars. |
|
|
1669
1698
|
| `withTagline` | `boolean \| undefined` | | `false` | Adds the tagline under the wordmark, separated from the fin by a divider. |
|
|
@@ -1819,6 +1848,7 @@ A series over time, with the fill fading out underneath it.
|
|
|
1819
1848
|
| `series` | `readonly ChartSeries[]` | yes | | |
|
|
1820
1849
|
| `stacked` | `boolean \| undefined` | | `false` | Adds the series up instead of overlaying them. |
|
|
1821
1850
|
| `summary` | `ReactNode` | | | What the chart says, in words. It goes in a visually hidden `figcaption`. |
|
|
1851
|
+
| `valueMax` | `number` | | | The top of the value axis, when the scale has one that the data does not reach — 100 for a percentage. |
|
|
1822
1852
|
| `xKey` | `string` | yes | | The key on the category axis: the day, the month, the course. |
|
|
1823
1853
|
| `xTickFormatter` | `(value: unknown) => string` | | | Formats the TICK on the category axis. Returns a string, because an axis tick is an SVG `<text>` and not a place a node can go. |
|
|
1824
1854
|
| `yTickFormatter` | `(value: unknown) => string` | | | Formats the tick on the VALUE axis — the currency symbol, the thousands separator, the percent sign. |
|
|
@@ -1839,6 +1869,7 @@ Bars, upright or lying down.
|
|
|
1839
1869
|
| `series` | `readonly ChartSeries[]` | yes | | |
|
|
1840
1870
|
| `stacked` | `boolean \| undefined` | | `false` | Stacks the series instead of putting them side by side. |
|
|
1841
1871
|
| `summary` | `ReactNode` | | | What the chart says, in words. It goes in a visually hidden `figcaption`. |
|
|
1872
|
+
| `valueMax` | `number` | | | The top of the value axis, when the scale has one that the data does not reach — 100 for a percentage. |
|
|
1842
1873
|
| `xKey` | `string` | yes | | The key on the category axis: the day, the month, the course. |
|
|
1843
1874
|
| `xTickFormatter` | `(value: unknown) => string` | | | Formats the TICK on the category axis. Returns a string, because an axis tick is an SVG `<text>` and not a place a node can go. |
|
|
1844
1875
|
| `yTickFormatter` | `(value: unknown) => string` | | | Formats the tick on the VALUE axis — the currency symbol, the thousands separator, the percent sign. |
|
|
@@ -1857,6 +1888,7 @@ Lines, for comparing series against each other.
|
|
|
1857
1888
|
| `legend` | `boolean \| undefined` | | | Shows the legend. It defaults to «only when there is more than one series»: a legend naming the one line already named by the chart's own heading is a row of pixels that says nothing. |
|
|
1858
1889
|
| `series` | `readonly ChartSeries[]` | yes | | |
|
|
1859
1890
|
| `summary` | `ReactNode` | | | What the chart says, in words. It goes in a visually hidden `figcaption`. |
|
|
1891
|
+
| `valueMax` | `number` | | | The top of the value axis, when the scale has one that the data does not reach — 100 for a percentage. |
|
|
1860
1892
|
| `xKey` | `string` | yes | | The key on the category axis: the day, the month, the course. |
|
|
1861
1893
|
| `xTickFormatter` | `(value: unknown) => string` | | | Formats the TICK on the category axis. Returns a string, because an axis tick is an SVG `<text>` and not a place a node can go. |
|
|
1862
1894
|
| `yTickFormatter` | `(value: unknown) => string` | | | Formats the tick on the VALUE axis — the currency symbol, the thousands separator, the percent sign. |
|
|
@@ -1937,7 +1969,7 @@ Types (2): `Theme`, `ThemeOptions`.
|
|
|
1937
1969
|
| `poseList` | `readonly ("desk" \| "laptop-coffee" \| "peek" \| "surf")[]` | |
|
|
1938
1970
|
| `poses` | `{ readonly desk: "pose-desk.png"; readonly 'laptop-coffee': "pose-laptop-coffee.png"; readonly peek: "pose-peek.png"; readonly surf: "pose-surf.png"; }` | Full-body poses. |
|
|
1939
1971
|
|
|
1940
|
-
Types (
|
|
1972
|
+
Types (9): `Background`, `Face`, `Fin`, `IsotypeBackground`, `IsotypeProps`, `LogoProps`, `MascotFaceProps`, `MascotProps`, `Pose`.
|
|
1941
1973
|
|
|
1942
1974
|
### `@eduardoalvarez/arrecife/icons`
|
|
1943
1975
|
|
package/package.json
CHANGED