@barmark/render 0.4.0 → 0.6.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 CHANGED
@@ -6,6 +6,27 @@ package uses [semantic versioning](https://semver.org/).
6
6
 
7
7
  ## [Unreleased]
8
8
 
9
+ ## [0.6.0] - 2026-11-02
10
+
11
+ Charts that scroll instead of wrapping. barmark.dev's home page uses it.
12
+
13
+ ### Added
14
+
15
+ - **`wrap: false`** (attribute `wrap="off"`, property `wrap`): a section
16
+ wider than the chart's box keeps its rows and columns rather than
17
+ wrapping them, and the chart overflows for its page to scroll.
18
+
19
+ ## [0.5.0] - 2026-11-02
20
+
21
+ The spoken name of a chord, for other tools. barmark.dev's /try page
22
+ uses it.
23
+
24
+ ### Added
25
+
26
+ - **`speakChord(record, key?)`**: the spoken name the chart gives a chord
27
+ ("C minor seven flat five"; with a key, its degree too), for screen
28
+ readers and other tools.
29
+
9
30
  ## [0.4.0] - 2026-11-01
10
31
 
11
32
  Chords as Nashville numbers or Roman numerals, and a little air between
@@ -158,7 +179,9 @@ that needs no script or as the `<barmark-chart>` web component. Requires
158
179
  zoom without sideways scrolling, the element renders and re-renders, and
159
180
  a screen reader hears "bar 9: D". Run them with `bun run test:browser`.
160
181
 
161
- [Unreleased]: https://github.com/kevinpeckham/barmark/compare/render-v0.4.0...HEAD
182
+ [Unreleased]: https://github.com/kevinpeckham/barmark/compare/render-v0.6.0...HEAD
183
+ [0.6.0]: https://github.com/kevinpeckham/barmark/compare/render-v0.5.0...render-v0.6.0
184
+ [0.5.0]: https://github.com/kevinpeckham/barmark/compare/render-v0.4.0...render-v0.5.0
162
185
  [0.4.0]: https://github.com/kevinpeckham/barmark/compare/render-v0.3.0...render-v0.4.0
163
186
  [0.3.0]: https://github.com/kevinpeckham/barmark/compare/render-v0.2.1...render-v0.3.0
164
187
  [0.2.1]: https://github.com/kevinpeckham/barmark/compare/render-v0.2.0...render-v0.2.1
package/README.md CHANGED
@@ -76,6 +76,7 @@ the element loads, with `<style data-barmark-font>` holding
76
76
  | `barNumbers` | `bar-numbers="off"` (`barNumbers`) | `true` | bar numbers at the start of each row |
77
77
  | `rehearsalLetters` | `rehearsal-letters` (`rehearsalLetters`) | `false` | boxed A, B, C… on each section as played |
78
78
  | `numerals` | `numerals` (`numerals`) | none | `"nashville"` (6m⁷) or `"roman"` (vi⁷): each chord as its degree in its bar's key |
79
+ | `wrap` | `wrap="off"` (`wrap`) | `true` | wrap a section's rows when it is wider than the chart's box; off, rows keep their columns and the chart overflows, for its page to scroll |
79
80
  | `diagnostics` | | `false` | list the chart's errors and warnings below it |
80
81
  | `fontUrl` | | none | emit Edwin's `@font-face` rule before the chart |
81
82
  | (the `text` argument) | (`text`) | `null` | the chart to draw; wins over `src` and the script child |
@@ -92,6 +93,21 @@ no key, note sets, N.C., `?` and `--` look as they always do, and a
92
93
  screen reader hears both: "bar 5: five, A seven". Numbered chords carry
93
94
  `data-numeral`. It's a view: the music and bar numbers don't change.
94
95
 
96
+ ### Spoken names
97
+
98
+ `speakChord(record, key?)` gives a chord the name a screen reader hears
99
+ in the chart, for other tools that want to say a chord aloud:
100
+
101
+ ```ts
102
+ import { parseChord } from "barmark";
103
+ import { speakChord } from "@barmark/render";
104
+
105
+ const r = parseChord("Cø");
106
+ if (r.ok) speakChord(r.value); // "C minor seven flat five"
107
+ ```
108
+
109
+ With a key, it speaks the degree too, as the numerals view does.
110
+
95
111
  ## Styling on purpose
96
112
 
97
113
  Set these custom properties on the element or anything above it:
package/dist/element.js CHANGED
@@ -14,6 +14,7 @@ export function optionsFromAttributes(el) {
14
14
  theme: THEMES.find((t) => t === theme) ?? "auto",
15
15
  barNumbers: el.getAttribute("bar-numbers") !== "off",
16
16
  rehearsalLetters: el.hasAttribute("rehearsal-letters"),
17
+ wrap: el.getAttribute("wrap") !== "off",
17
18
  ...(numerals ? { numerals } : {}),
18
19
  };
19
20
  }
@@ -48,7 +49,14 @@ async function fetchText(src) {
48
49
  }
49
50
  function defineElement() {
50
51
  class BarmarkChart extends HTMLElement {
51
- static observedAttributes = ["src", "theme", "bar-numbers", "rehearsal-letters", "numerals"];
52
+ static observedAttributes = [
53
+ "src",
54
+ "theme",
55
+ "bar-numbers",
56
+ "rehearsal-letters",
57
+ "numerals",
58
+ "wrap",
59
+ ];
52
60
  #observer = new MutationObserver(() => this.#schedule());
53
61
  #renders = 0;
54
62
  #scheduled = false;
@@ -103,6 +111,15 @@ function defineElement() {
103
111
  else
104
112
  this.setAttribute("bar-numbers", "off");
105
113
  }
114
+ get wrap() {
115
+ return optionsFromAttributes(this).wrap ?? true;
116
+ }
117
+ set wrap(v) {
118
+ if (v)
119
+ this.removeAttribute("wrap");
120
+ else
121
+ this.setAttribute("wrap", "off");
122
+ }
106
123
  get rehearsalLetters() {
107
124
  return this.hasAttribute("rehearsal-letters");
108
125
  }
@@ -167,6 +184,10 @@ function defineElement() {
167
184
  chart?.setAttribute("data-bar-numbers", "off");
168
185
  else
169
186
  chart?.removeAttribute("data-bar-numbers");
187
+ if (options.wrap === false)
188
+ chart?.setAttribute("data-wrap", "off");
189
+ else
190
+ chart?.removeAttribute("data-wrap");
170
191
  }
171
192
  }
172
193
  customElements.define("barmark-chart", BarmarkChart);
package/dist/html.d.ts CHANGED
@@ -9,6 +9,11 @@ export type RenderOptions = {
9
9
  fontUrl?: string;
10
10
  /** Show each chord as its degree in the bar's key, where the chart names one. Off by default. */
11
11
  numerals?: Numerals;
12
+ /**
13
+ * Wrap a section's rows when it is wider than the chart's box (the default).
14
+ * With `false` rows keep their columns, and the page scrolls the chart instead.
15
+ */
16
+ wrap?: boolean;
12
17
  };
13
18
  export declare function renderChart(text: string, options?: RenderOptions): string;
14
19
  export declare const THEMES: readonly ["auto", "light", "dark"];
package/dist/html.js CHANGED
@@ -15,6 +15,7 @@ function elementAttributes(options) {
15
15
  options.barNumbers === false ? ' bar-numbers="off"' : "",
16
16
  options.rehearsalLetters ? " rehearsal-letters" : "",
17
17
  numeralsOf(options) ? ` numerals="${numeralsOf(options)}"` : "",
18
+ options.wrap === false ? ' wrap="off"' : "",
18
19
  ].join("");
19
20
  }
20
21
  export const THEMES = ["auto", "light", "dark"];
@@ -35,7 +36,7 @@ function chartHtml(text, options) {
35
36
  barNumbers: options.barNumbers ?? true,
36
37
  numerals: numeralsOf(options),
37
38
  };
38
- return `<div class="bm-chart" data-theme="${themeOf(options)}"${settings.barNumbers ? "" : ' data-bar-numbers="off"'}>${headerHtml(model)}${model.blocks.map((b) => blockHtml(b, settings)).join("")}${options.diagnostics ? diagnosticsHtml(model) : ""}</div>`;
39
+ return `<div class="bm-chart" data-theme="${themeOf(options)}"${settings.barNumbers ? "" : ' data-bar-numbers="off"'}${options.wrap === false ? ' data-wrap="off"' : ""}>${headerHtml(model)}${model.blocks.map((b) => blockHtml(b, settings)).join("")}${options.diagnostics ? diagnosticsHtml(model) : ""}</div>`;
39
40
  }
40
41
  function headerHtml(model) {
41
42
  const title = model.title
package/dist/index.d.ts CHANGED
@@ -2,5 +2,6 @@ export type { Numerals } from "./chord.js";
2
2
  export { FONT_FAMILY, fontFaceCss } from "./font.js";
3
3
  export { type RenderOptions, renderChart, renderChartUnwrapped } from "./html.js";
4
4
  export { layoutChart } from "./model.js";
5
+ export { speakChord } from "./speech.js";
5
6
  export { stylesheet } from "./styles.js";
6
7
  export type * from "./types.js";
package/dist/index.js CHANGED
@@ -1,4 +1,5 @@
1
1
  export { FONT_FAMILY, fontFaceCss } from "./font.js";
2
2
  export { renderChart, renderChartUnwrapped } from "./html.js";
3
3
  export { layoutChart } from "./model.js";
4
+ export { speakChord } from "./speech.js";
4
5
  export { stylesheet } from "./styles.js";
package/dist/styles.js CHANGED
@@ -84,6 +84,7 @@ const css = `
84
84
  .bm-times{font:italic 1.2em/1 Georgia,serif;text-transform:none;letter-spacing:0;color:var(--bm-muted)}
85
85
  .bm-grid{display:grid;grid-template-columns:${em(GUTTER)} repeat(var(--bm-cols),auto) auto;width:max-content;max-width:100%}
86
86
  .bm-chart[data-bar-numbers="off"] .bm-grid{grid-template-columns:repeat(var(--bm-cols),auto) auto}
87
+ .bm-chart[data-wrap="off"] .bm-grid{max-width:none}
87
88
  .bm-row{display:contents}
88
89
  .bm-row+.bm-row>*{margin-top:${em(ROW_GAP)}}
89
90
  .bm-row+.bm-row>.bm-bar-number{margin-top:${at(SIZES.barNumber, ROW_GAP)}}
@@ -131,7 +132,7 @@ const css = `
131
132
  .bm-bar[data-cut]{outline-color:CanvasText}
132
133
  }
133
134
  ${FIT_STEPS.map((w) => {
134
- const g = `.bm-grid[data-fit="${w}"]`;
135
+ const g = `.bm-chart:not([data-wrap="off"]) .bm-grid[data-fit="${w}"]`;
135
136
  return `@container bm (max-width:${w}em){${g}{display:block}${g}>.bm-row{display:flex;flex-wrap:wrap;row-gap:${em(ROW_GAP)}}${g} .bm-empty{display:none}${g} .bm-bar{flex:0 1 auto;max-width:100%;border-left:.075em solid var(--bm-rule);margin-left:-.075em}${g} :is(.bm-bar-body,.bm-group,.bm-notes){flex-wrap:wrap}${g} .bm-bar-body>*{flex:1 1 auto;min-width:auto}}`;
136
137
  }).join("\n")}
137
138
  `;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@barmark/render",
3
- "version": "0.4.0",
3
+ "version": "0.6.0",
4
4
  "description": "Render barmark chord charts as a clean, accessible bar grid: HTML with Declarative Shadow DOM, or the <barmark-chart> web component.",
5
5
  "type": "module",
6
6
  "exports": {