@howells/hairline 0.1.0 → 0.2.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 ADDED
@@ -0,0 +1,7 @@
1
+ # @howells/hairline
2
+
3
+ ## 0.2.0
4
+
5
+ ### Minor Changes
6
+
7
+ - 7f6e859: A scaled chart now keeps its rules at one device pixel by default. When `scale` is above 1 or `fit` is set, `strokes` defaults to `"device"`: grid lines, baselines, lines and rings stay hairlines however large the chart is shown, while type and textures still scale with the composition and filled marks such as the bars of RungBars are unaffected. Unscaled charts draw exactly as before. To keep the old behaviour on a scaled chart, pass `strokes="proportional"`.
package/README.md CHANGED
@@ -90,7 +90,7 @@ import { RungBars } from "@howells/hairline/rung-bars";
90
90
  - **Data is required.** The package never falls back to demo data. The six charts with schemas take a `data` (or, for `Slope`, `rows`) prop that a zod 4 schema validates; see the chart's page on the docs site.
91
91
  - **The schema contract.** `@howells/hairline/schema` is React-free: one schema per chart, the types inferred from it, `validate` returning path-specific issues, formatter descriptors (`"percent"`, or Intl number-format options; functions still work from TypeScript), and a `registry` of each chart's `kind`, Visual Vocabulary job, data shape, limits and guidance. The JSON Schemas are emitted at build to `lib/json-schema/<kind>.json` (`@howells/hairline/json-schema/<kind>.json`), with an `index.json`.
92
92
  - **Server mode.** Pass `width` and `height` for a fixed-size chart that is complete in the server's first HTML (`renderToString` returns the drawn chart), and it is drawn and ready (`data-hl-ready="true"`) with no client: a fixed-size chart defaults to `animate="off"`, while `animate="scroll"` or `"mount"` asks for the entry animation (then it starts hidden). The responsive chart defaults to `"scroll"`. Omit `width` for the measured, responsive chart, and give it `initialWidth` so its first render is real. A summary and a data table are in the markup for screen readers; `table` shows the table to everyone. Every module starts with `"use client"`, so a server component can render a chart whose props are plain data. A formatter function cannot cross that boundary, so use a descriptor, or wrap the chart in a client component of your own.
93
- - **Export mode.** For slides and rendered images, draw at the logical `width` and set `scale` to show the whole composition larger through the viewBox, type and textures with it. `fit` fills the container at the chart's aspect ratio. `strokes="device"` keeps every stroke at one device pixel however large the chart is shown. It acts on stroked geometry only (rules, lines, rings, outlines); filled marks such as RungBars' bars scale with the composition either way, so there it changes just the baseline rule. `animate="off"` and `onReady` tell a renderer when the chart has settled.
93
+ - **Export mode.** For slides and rendered images, draw at the logical `width` and set `scale` to show the whole composition larger through the viewBox, type and textures with it. `fit` fills the container at the chart's aspect ratio. A scaled chart (`scale` or `fit`) keeps every stroke at one device pixel however large it is shown: `strokes="device"` is its default, and `strokes="proportional"` lets strokes grow with the composition. An unscaled chart is unchanged. The policy acts on stroked geometry only (rules, lines, rings, outlines); filled marks such as RungBars' bars scale with the composition either way, so there it changes just the baseline rule. `animate="off"` and `onReady` tell a renderer when the chart has settled.
94
94
  - **Several roots on one page.** `idPrefix` leads every SVG id, so charts rendered in separate React roots do not collide.
95
95
  - **Fonts.** `deriveTheme`'s `font`, or `--hl-font`, sets the charts' face. Label widths are measured for Inter, so another face may crowd; pass `metrics` with its glyph widths.
96
96
  - **Emphasis.** The hero is chosen by a datum's key (`id`, else its label or date), never by index.
package/lib/charts/svg.js CHANGED
@@ -5,7 +5,7 @@ import { useContext as n } from "react";
5
5
  import { jsx as r, jsxs as i } from "react/jsx-runtime";
6
6
  //#region src/charts/svg.tsx
7
7
  var a = ({ children: a, desc: o, height: s, label: c, leave: l, style: u, title: d, width: f, ...p }) => {
8
- let m = e("desc"), { fit: h, scale: g, strokes: _ } = n(t), v = h === !0 || g !== void 0 ? {
8
+ let m = e("desc"), { fit: h, scale: g, strokes: _ } = n(t), v = h === !0 || g !== void 0, y = _ ?? (v ? "device" : "proportional"), b = v ? {
9
9
  height: h === !0 ? void 0 : s * (g ?? 1),
10
10
  style: h === !0 ? {
11
11
  ...u,
@@ -20,8 +20,8 @@ var a = ({ children: a, desc: o, height: s, label: c, leave: l, style: u, title:
20
20
  width: f
21
21
  };
22
22
  return /* @__PURE__ */ i("svg", {
23
- "data-hl-strokes": _ === "device" ? "device" : void 0,
24
- ...v,
23
+ "data-hl-strokes": y === "device" ? "device" : void 0,
24
+ ...b,
25
25
  "aria-describedby": o === void 0 ? void 0 : m,
26
26
  "aria-label": c,
27
27
  onPointerLeave: l === void 0 ? void 0 : () => {
@@ -37,7 +37,7 @@
37
37
  "exclusiveMinimum": 0
38
38
  },
39
39
  "strokes": {
40
- "description": "\"proportional\" (default) scales strokes with the composition; \"device\" keeps every stroke at one device pixel. It acts on stroked geometry only (rules, lines, rings, outlines): filled marks, such as the bars of RungBars, scale either way.",
40
+ "description": "\"device\" keeps every stroke at one device pixel, and is the default when the chart is scaled (`scale` or `fit`); \"proportional\" scales strokes with the composition. It acts on stroked geometry only (rules, lines, rings, outlines): filled marks, such as the bars of RungBars, scale either way.",
41
41
  "type": "string",
42
42
  "enum": [
43
43
  "proportional",
@@ -37,7 +37,7 @@
37
37
  "exclusiveMinimum": 0
38
38
  },
39
39
  "strokes": {
40
- "description": "\"proportional\" (default) scales strokes with the composition; \"device\" keeps every stroke at one device pixel. It acts on stroked geometry only (rules, lines, rings, outlines): filled marks, such as the bars of RungBars, scale either way.",
40
+ "description": "\"device\" keeps every stroke at one device pixel, and is the default when the chart is scaled (`scale` or `fit`); \"proportional\" scales strokes with the composition. It acts on stroked geometry only (rules, lines, rings, outlines): filled marks, such as the bars of RungBars, scale either way.",
41
41
  "type": "string",
42
42
  "enum": [
43
43
  "proportional",
@@ -37,7 +37,7 @@
37
37
  "exclusiveMinimum": 0
38
38
  },
39
39
  "strokes": {
40
- "description": "\"proportional\" (default) scales strokes with the composition; \"device\" keeps every stroke at one device pixel. It acts on stroked geometry only (rules, lines, rings, outlines): filled marks, such as the bars of RungBars, scale either way.",
40
+ "description": "\"device\" keeps every stroke at one device pixel, and is the default when the chart is scaled (`scale` or `fit`); \"proportional\" scales strokes with the composition. It acts on stroked geometry only (rules, lines, rings, outlines): filled marks, such as the bars of RungBars, scale either way.",
41
41
  "type": "string",
42
42
  "enum": [
43
43
  "proportional",
@@ -37,7 +37,7 @@
37
37
  "exclusiveMinimum": 0
38
38
  },
39
39
  "strokes": {
40
- "description": "\"proportional\" (default) scales strokes with the composition; \"device\" keeps every stroke at one device pixel. It acts on stroked geometry only (rules, lines, rings, outlines): filled marks, such as the bars of RungBars, scale either way.",
40
+ "description": "\"device\" keeps every stroke at one device pixel, and is the default when the chart is scaled (`scale` or `fit`); \"proportional\" scales strokes with the composition. It acts on stroked geometry only (rules, lines, rings, outlines): filled marks, such as the bars of RungBars, scale either way.",
41
41
  "type": "string",
42
42
  "enum": [
43
43
  "proportional",
@@ -37,7 +37,7 @@
37
37
  "exclusiveMinimum": 0
38
38
  },
39
39
  "strokes": {
40
- "description": "\"proportional\" (default) scales strokes with the composition; \"device\" keeps every stroke at one device pixel. It acts on stroked geometry only (rules, lines, rings, outlines): filled marks, such as the bars of RungBars, scale either way.",
40
+ "description": "\"device\" keeps every stroke at one device pixel, and is the default when the chart is scaled (`scale` or `fit`); \"proportional\" scales strokes with the composition. It acts on stroked geometry only (rules, lines, rings, outlines): filled marks, such as the bars of RungBars, scale either way.",
41
41
  "type": "string",
42
42
  "enum": [
43
43
  "proportional",
@@ -28,7 +28,7 @@
28
28
  "exclusiveMinimum": 0
29
29
  },
30
30
  "strokes": {
31
- "description": "\"proportional\" (default) scales strokes with the composition; \"device\" keeps every stroke at one device pixel. It acts on stroked geometry only (rules, lines, rings, outlines): filled marks, such as the bars of RungBars, scale either way.",
31
+ "description": "\"device\" keeps every stroke at one device pixel, and is the default when the chart is scaled (`scale` or `fit`); \"proportional\" scales strokes with the composition. It acts on stroked geometry only (rules, lines, rings, outlines): filled marks, such as the bars of RungBars, scale either way.",
32
32
  "type": "string",
33
33
  "enum": [
34
34
  "proportional",
@@ -3,7 +3,7 @@ import { z as e } from "zod";
3
3
  var t = {
4
4
  fit: e.boolean().optional().describe("Fill the container's width at the chart's aspect ratio. Wins over `scale`."),
5
5
  scale: e.number().positive().optional().describe("Slides: draw at the logical width, then show the composition this many times larger, type and textures with it."),
6
- strokes: e.enum(["proportional", "device"]).optional().describe("\"proportional\" (default) scales strokes with the composition; \"device\" keeps every stroke at one device pixel. It acts on stroked geometry only (rules, lines, rings, outlines): filled marks, such as the bars of RungBars, scale either way.")
6
+ strokes: e.enum(["proportional", "device"]).optional().describe("\"device\" keeps every stroke at one device pixel, and is the default when the chart is scaled (`scale` or `fit`); \"proportional\" scales strokes with the composition. It acts on stroked geometry only (rules, lines, rings, outlines): filled marks, such as the bars of RungBars, scale either way.")
7
7
  };
8
8
  //#endregion
9
9
  export { t as scaleShape };
@@ -12,8 +12,10 @@ export declare const scaleShape: {
12
12
  */
13
13
  scale: z.ZodOptional<z.ZodNumber>;
14
14
  /**
15
- * "proportional" (default) lets strokes scale with the composition;
16
- * "device" keeps every stroke at one device pixel however large it is shown.
15
+ * "device" keeps every stroke at one device pixel however large it is shown;
16
+ * it is the default once the chart is scaled (`scale` or `fit`).
17
+ * "proportional" lets strokes scale with the composition, and is what an
18
+ * unscaled chart draws.
17
19
  * The policy acts on stroked geometry only: rules, grid lines, lines and
18
20
  * paths, rings and outlines. Filled marks (the bars of RungBars and the
19
21
  * stems of HairlineArea are rects, the textures are fills) scale with the
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@howells/hairline",
3
- "version": "0.1.0",
3
+ "version": "0.2.0",
4
4
  "description": "An engraved chart library for React: pixel-snapped hairlines, one ink hero per chart, and banknote textures.",
5
5
  "homepage": "https://hairline.danielhowells.com",
6
6
  "license": "MIT",
@@ -78,22 +78,6 @@
78
78
  "access": "public",
79
79
  "provenance": true
80
80
  },
81
- "scripts": {
82
- "dev": "vite",
83
- "build": "tsc -b && vite build",
84
- "preview": "vite preview",
85
- "typecheck": "tsc -b && tsc -p scripts && tsc -p acceptance && tsc -p site",
86
- "lint": "howells-check .",
87
- "lint:fix": "howells-fix .",
88
- "build:lib": "node scripts/library-index.ts && howells-oxfmt package.json src/index.ts src/kit.ts src/maps.ts src/schema.ts src/entries && vite build -c vite.lib.config.ts && tsc -p tsconfig.lib.json && node scripts/library-package.ts && node scripts/library-css.ts && node scripts/library-schema.ts",
89
- "changeset": "changeset",
90
- "version-packages": "changeset version",
91
- "release": "pnpm build:lib && changeset publish",
92
- "test": "pnpm build:lib && tsc -p tests && vitest run",
93
- "e2e": "acceptance/docker.sh",
94
- "generate:baselines": "acceptance/docker.sh visual.spec.ts --update-snapshots",
95
- "generate:artefacts": "playwright test -c acceptance/artefacts.config.ts"
96
- },
97
81
  "dependencies": {
98
82
  "d3-array": "^3.2.4",
99
83
  "d3-chord": "^3.0.1",
@@ -160,5 +144,20 @@
160
144
  "react": "^19.0.0",
161
145
  "react-dom": "^19.0.0"
162
146
  },
163
- "packageManager": "pnpm@9.0.0"
164
- }
147
+ "scripts": {
148
+ "dev": "vite",
149
+ "build": "tsc -b && vite build",
150
+ "preview": "vite preview",
151
+ "typecheck": "tsc -b && tsc -p scripts && tsc -p acceptance && tsc -p site",
152
+ "lint": "howells-check .",
153
+ "lint:fix": "howells-fix .",
154
+ "build:lib": "node scripts/library-index.ts && howells-oxfmt package.json src/index.ts src/kit.ts src/maps.ts src/schema.ts src/entries && vite build -c vite.lib.config.ts && tsc -p tsconfig.lib.json && node scripts/library-package.ts && node scripts/library-css.ts && node scripts/library-schema.ts",
155
+ "changeset": "changeset",
156
+ "version-packages": "changeset version",
157
+ "release": "pnpm build:lib && changeset publish",
158
+ "test": "pnpm build:lib && tsc -p tests && vitest run",
159
+ "e2e": "acceptance/docker.sh",
160
+ "generate:baselines": "acceptance/docker.sh visual.spec.ts --update-snapshots",
161
+ "generate:artefacts": "playwright test -c acceptance/artefacts.config.ts"
162
+ }
163
+ }