From d076aec856921c2fe8a5204a0c31580a846af180 Mon Sep 17 00:00:00 2001
From: Chris Swithinbank
Date: Thu, 10 Aug 2023 13:02:50 +0200
Subject: Add theme editor to styling guide (#470)
---
.changeset/red-rockets-turn.md | 26 ++++
docs/package.json | 2 +
docs/src/components/theme-designer.astro | 147 +++++++++++++++++++++
docs/src/components/theme-designer/atom.ts | 31 +++++
.../components/theme-designer/color-editor.astro | 93 +++++++++++++
docs/src/components/theme-designer/color-lib.ts | 90 +++++++++++++
docs/src/components/theme-designer/palette.astro | 41 ++++++
docs/src/components/theme-designer/presets.astro | 94 +++++++++++++
docs/src/components/theme-designer/preview.astro | 53 ++++++++
docs/src/components/theme-designer/store.ts | 47 +++++++
.../components/theme-designer/value-slider.astro | 62 +++++++++
docs/src/content/docs/guides/css-and-tailwind.mdx | 59 ++++++++-
packages/starlight/components/Icons.ts | 2 +
packages/starlight/style/props.css | 43 +++---
pnpm-lock.yaml | 15 +++
15 files changed, 781 insertions(+), 24 deletions(-)
create mode 100644 .changeset/red-rockets-turn.md
create mode 100644 docs/src/components/theme-designer.astro
create mode 100644 docs/src/components/theme-designer/atom.ts
create mode 100644 docs/src/components/theme-designer/color-editor.astro
create mode 100644 docs/src/components/theme-designer/color-lib.ts
create mode 100644 docs/src/components/theme-designer/palette.astro
create mode 100644 docs/src/components/theme-designer/presets.astro
create mode 100644 docs/src/components/theme-designer/preview.astro
create mode 100644 docs/src/components/theme-designer/store.ts
create mode 100644 docs/src/components/theme-designer/value-slider.astro
diff --git a/.changeset/red-rockets-turn.md b/.changeset/red-rockets-turn.md
new file mode 100644
index 00000000..de6bbe44
--- /dev/null
+++ b/.changeset/red-rockets-turn.md
@@ -0,0 +1,26 @@
+---
+'@astrojs/starlight': minor
+---
+
+Drop support for the `--sl-hue-accent` CSS custom property.
+
+⚠️ **BREAKING CHANGE** — In previous Starlight versions you could control the accent color by setting the `--sl-hue-accent` custom property. This could result in inaccessible color contrast and unpredictable results.
+
+You must now set accent colors directly. If you relied on setting `--sl-hue-accent`, migrate by setting light and dark mode colors in your custom CSS:
+
+```css
+:root {
+ --sl-hue-accent: 234;
+ --sl-color-accent-low: hsl(var(--sl-hue-accent), 54%, 20%);
+ --sl-color-accent: hsl(var(--sl-hue-accent), 100%, 60%);
+ --sl-color-accent-high: hsl(var(--sl-hue-accent), 100%, 87%);
+}
+
+:root[data-theme="light"] {
+ --sl-color-accent-high: hsl(var(--sl-hue-accent), 80%, 30%);
+ --sl-color-accent: hsl(var(--sl-hue-accent), 90%, 60%);
+ --sl-color-accent-low: hsl(var(--sl-hue-accent), 88%, 90%);
+}
+```
+
+The [new color theme editor](https://starlight.astro.build/guides/css-and-tailwind/#color-theme-editor) might help if you’d prefer to set a new color scheme.
\ No newline at end of file
diff --git a/docs/package.json b/docs/package.json
index bcfc4904..bed55508 100644
--- a/docs/package.json
+++ b/docs/package.json
@@ -14,7 +14,9 @@
},
"dependencies": {
"@astrojs/starlight": "workspace:*",
+ "@types/culori": "^2.0.0",
"astro": "^2.10.4",
+ "culori": "^3.2.0",
"sharp": "^0.32.3"
},
"devDependencies": {
diff --git a/docs/src/components/theme-designer.astro b/docs/src/components/theme-designer.astro
new file mode 100644
index 00000000..b8bd3b31
--- /dev/null
+++ b/docs/src/components/theme-designer.astro
@@ -0,0 +1,147 @@
+---
+import { TabItem, Tabs } from '@astrojs/starlight/components';
+import ColorEditor, { Props as EditorProps } from './theme-designer/color-editor.astro';
+import Presets, { Props as PresetsProps } from './theme-designer/presets.astro';
+import Preview from './theme-designer/preview.astro';
+
+interface Props {
+ labels: {
+ presets: PresetsProps['labels'];
+ editor: EditorProps['labels'] & { accentColor: string; grayColor: string };
+ preview: Record<
+ 'darkMode' | 'lightMode' | 'bodyText' | 'linkText' | 'dimText' | 'inlineCode',
+ string
+ >;
+ };
+}
+const {
+ labels: { presets, editor, preview },
+} = Astro.props;
+---
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/docs/src/components/theme-designer/atom.ts b/docs/src/components/theme-designer/atom.ts
new file mode 100644
index 00000000..e1f2518c
--- /dev/null
+++ b/docs/src/components/theme-designer/atom.ts
@@ -0,0 +1,31 @@
+class Atom {
+ #v: T;
+ #subscribers = new Map<(v: T) => void, (v: T) => void>();
+ #notify = () => this.#subscribers.forEach((cb) => cb(this.#v));
+ constructor(init: T) {
+ this.#v = init;
+ }
+ get(): T {
+ return this.#v;
+ }
+ set(v: T): void {
+ this.#v = v;
+ this.#notify();
+ }
+ subscribe(cb: (v: T) => void): () => boolean {
+ cb(this.#v);
+ this.#subscribers.set(cb, cb);
+ return () => this.#subscribers.delete(cb);
+ }
+}
+
+type MapStore = Atom & { setKey: (key: keyof T, value: T[typeof key]) => void };
+
+export function map>(value: T): MapStore {
+ const atom = new Atom(value) as MapStore;
+ atom.setKey = (key: keyof T, value: T[typeof key]) => {
+ const curr = atom.get();
+ if (curr[key] !== value) atom.set({ ...curr, [key]: value });
+ };
+ return atom;
+}
diff --git a/docs/src/components/theme-designer/color-editor.astro b/docs/src/components/theme-designer/color-editor.astro
new file mode 100644
index 00000000..e91557ed
--- /dev/null
+++ b/docs/src/components/theme-designer/color-editor.astro
@@ -0,0 +1,93 @@
+---
+import { oklchToHex } from './color-lib';
+import { store } from './store';
+import ValueSlider from './value-slider.astro';
+
+export interface Props {
+ key: keyof typeof store;
+ legend: string;
+ labels: Record<'pickColor' | 'hue' | 'chroma', string>;
+}
+const { key, legend, labels } = Astro.props;
+const { hue, chroma } = store[key].get();
+const initialColor = oklchToHex(52, chroma, hue);
+---
+
+
+
+
+
+
+
+
diff --git a/docs/src/components/theme-designer/color-lib.ts b/docs/src/components/theme-designer/color-lib.ts
new file mode 100644
index 00000000..27b52b52
--- /dev/null
+++ b/docs/src/components/theme-designer/color-lib.ts
@@ -0,0 +1,90 @@
+import { useMode, modeOklch, modeRgb, formatHex, clampChroma } from 'culori/fn';
+
+const rgb = useMode(modeRgb);
+export const oklch = useMode(modeOklch);
+
+/** Convert an OKLCH color to an RGB hex code. */
+export const oklchToHex = (l: number, c: number, h: number) => {
+ const okLchColor = oklch(`oklch(${l}% ${c} ${h})`)!;
+ const rgbColor = rgb(clampChroma(okLchColor, 'oklch'));
+ return formatHex(rgbColor);
+};
+
+/** Generate dark and light palettes based on user-selected hue and chroma values. */
+export function getPalettes(config: {
+ accent: { hue: number; chroma: number };
+ gray: { hue: number; chroma: number };
+}) {
+ const {
+ accent: { hue: ah, chroma: ac },
+ gray: { hue: gh, chroma: gc },
+ } = config;
+ return {
+ dark: {
+ // Accents
+ 'accent-low': oklchToHex(25.94, ac / 3, ah),
+ accent: oklchToHex(52.28, ac, ah),
+ 'accent-high': oklchToHex(83.38, ac / 3, ah),
+ // Grays
+ white: oklchToHex(100, 0, 0),
+ 'gray-1': oklchToHex(94.77, gc / 2.5, gh),
+ 'gray-2': oklchToHex(81.34, gc / 2, gh),
+ 'gray-3': oklchToHex(63.78, gc, gh),
+ 'gray-4': oklchToHex(46.01, gc, gh),
+ 'gray-5': oklchToHex(34.09, gc, gh),
+ 'gray-6': oklchToHex(27.14, gc, gh),
+ black: oklchToHex(20.94, gc / 2, gh),
+ },
+ light: {
+ // Accents
+ 'accent-low': oklchToHex(87.81, ac / 4, ah),
+ accent: oklchToHex(52.95, ac, ah),
+ 'accent-high': oklchToHex(31.77, ac / 2, ah),
+ // Grays
+ white: oklchToHex(20.94, gc / 2, gh),
+ 'gray-1': oklchToHex(27.14, gc, gh),
+ 'gray-2': oklchToHex(34.09, gc, gh),
+ 'gray-3': oklchToHex(46.01, gc, gh),
+ 'gray-4': oklchToHex(63.78, gc, gh),
+ 'gray-5': oklchToHex(81.34, gc / 2, gh),
+ 'gray-6': oklchToHex(94.77, gc / 2.5, gh),
+ 'gray-7': oklchToHex(97.35, gc / 5, gh),
+ black: oklchToHex(100, 0, 0),
+ },
+ };
+}
+
+/*
+This is the default Starlight color palette in OKLCH.
+Used as a reference when designing the algorithm for mapping
+user hue and chroma pairs to a similar palette.
+
+Dark mode:
+ accent lo oklch(25.94% 0.09 273.5)
+ accent oklch(52.28% 0.266 268.7)
+ accent hi oklch(83.38% 0.084 279.5)
+
+ white oklch(100% 0 0)
+ gray-1 oklch(94.77% 0.008 278.19)
+ gray-2 oklch(81.34% 0.011 274.87)
+ gray-3 oklch(63.78% 0.019 265.84)
+ gray-4 oklch(46.01% 0.021 270.93)
+ gray-5 oklch(34.09% 0.017 267.07)
+ gray-6 oklch(27.14% 0.015 267.03)
+ black oklch(20.94% 0.01 268.4)
+
+Light mode:
+ accent lo oklch(87.81% 0.056 280.2)
+ accent oklch(52.95% 0.243 270.2)
+ accent hi oklch(31.77% 0.177 267.2)
+
+ white oklch(20.94% 0.01 268.4)
+ gray-1 oklch(27.14% 0.015 267.03)
+ gray-2 oklch(34.09% 0.017 267.07)
+ gray-3 oklch(46.01% 0.021 270.93)
+ gray-4 oklch(63.78% 0.019 265.84)
+ gray-5 oklch(81.34% 0.011 274.87)
+ gray-6 oklch(94.77% 0.008 278.19)
+ gray-7 oklch(97.35% 0.004 286.32)
+ black oklch(100% 0 0)
+*/
diff --git a/docs/src/components/theme-designer/palette.astro b/docs/src/components/theme-designer/palette.astro
new file mode 100644
index 00000000..0b4e1d0b
--- /dev/null
+++ b/docs/src/components/theme-designer/palette.astro
@@ -0,0 +1,41 @@
+---
+interface Props {
+ light?: boolean;
+}
+---
+
+
+
+ {
+ Astro.props.light ? (
+
+ ) : (
+
+ )
+ }
+
+
+
+
+
+
+
+
+ {Astro.props.light &&
}
+
+
+
+
diff --git a/docs/src/components/theme-designer/presets.astro b/docs/src/components/theme-designer/presets.astro
new file mode 100644
index 00000000..5925df37
--- /dev/null
+++ b/docs/src/components/theme-designer/presets.astro
@@ -0,0 +1,94 @@
+---
+import { Icon } from '@astrojs/starlight/components';
+import { getPalettes } from './color-lib';
+import { presets } from './store';
+
+export interface Props {
+ labels: Record & {
+ label: string;
+ random: string;
+ };
+}
+const { labels } = Astro.props;
+
+const resolvedPresets = Object.entries(presets).map(([key, preset]) => {
+ const palette = getPalettes(preset);
+ const label = labels[key as keyof typeof presets];
+ return [key, { ...palette, label }] as const;
+});
+---
+
+
+ {labels.label}
+ {
+ resolvedPresets.map(([key, { label, dark, light }]) => (
+
+ ))
+ }
+
+
+
+
+
+
diff --git a/docs/src/components/theme-designer/preview.astro b/docs/src/components/theme-designer/preview.astro
new file mode 100644
index 00000000..7dbfb7d8
--- /dev/null
+++ b/docs/src/components/theme-designer/preview.astro
@@ -0,0 +1,53 @@
+---
+import type { HTMLAttributes } from 'astro/types';
+import Palette from './palette.astro';
+
+interface Props extends HTMLAttributes<'div'> {
+ labels: {
+ lightMode: string;
+ darkMode: string;
+ bodyText: string;
+ linkText: string;
+ dimText: string;
+ inlineCode: string;
+ };
+}
+const { labels, ...attrs } = Astro.props;
+const light = 'data-light' in attrs;
+const linkColor = light ? '--sl-color-accent' : '--sl-color-accent-high';
+const codeBg = light ? '--sl-color-gray-6' : '--sl-color-gray-5';
+---
+
+
+
{light ? labels.lightMode : labels.darkMode}
+
+ {labels.bodyText}
+ {labels.linkText}
+ {labels.dimText}
+ {labels.inlineCode}
+
+
+
+
+
diff --git a/docs/src/components/theme-designer/store.ts b/docs/src/components/theme-designer/store.ts
new file mode 100644
index 00000000..6eb1aa82
--- /dev/null
+++ b/docs/src/components/theme-designer/store.ts
@@ -0,0 +1,47 @@
+import { map } from './atom';
+
+export const presets = {
+ ocean: {
+ accent: { hue: 240, chroma: 0.27 },
+ gray: { hue: 220, chroma: 0.025 },
+ },
+ forest: {
+ accent: { hue: 140, chroma: 0.27 },
+ gray: { hue: 140, chroma: 0.03 },
+ },
+ oxide: {
+ accent: { hue: 30, chroma: 0.27 },
+ gray: { hue: 30, chroma: 0.02 },
+ },
+ nebula: {
+ accent: { hue: 320, chroma: 0.27 },
+ gray: { hue: 305, chroma: 0.07 },
+ },
+ default: {
+ accent: { hue: 269, chroma: 0.27 },
+ gray: { hue: 270, chroma: 0.016 },
+ },
+};
+
+export const store = {
+ accent: map(presets.default.accent),
+ gray: map(presets.default.gray),
+};
+
+export const usePreset = (name: string) => {
+ if (name in presets) {
+ const { accent, gray } = presets[name as keyof typeof presets];
+ store.accent.set(accent);
+ store.gray.set(gray);
+ }
+};
+
+const MAX_CHROMA = 0.27;
+
+export const useRandom = () => {
+ store.accent.set({ hue: randomHue(), chroma: MAX_CHROMA - randomChroma() });
+ store.gray.set({ hue: randomHue(), chroma: randomChroma() });
+};
+
+const randomHue = () => Math.round(Math.random() * 360);
+const randomChroma = () => Math.pow(Math.random(), 3) * MAX_CHROMA;
diff --git a/docs/src/components/theme-designer/value-slider.astro b/docs/src/components/theme-designer/value-slider.astro
new file mode 100644
index 00000000..4202f029
--- /dev/null
+++ b/docs/src/components/theme-designer/value-slider.astro
@@ -0,0 +1,62 @@
+---
+import { store } from './store';
+
+interface Props {
+ label: string;
+ storeKey: keyof typeof store;
+ type: 'hue' | 'chroma';
+}
+const { label, storeKey, type } = Astro.props;
+
+const { max, step } = { hue: { max: 360, step: 1 }, chroma: { max: 0.27, step: 0.001 } }[type];
+const value = store[storeKey].get()[type];
+---
+
+
+
+
+
+
+
+
diff --git a/docs/src/content/docs/guides/css-and-tailwind.mdx b/docs/src/content/docs/guides/css-and-tailwind.mdx
index fc04b23e..d7096875 100644
--- a/docs/src/content/docs/guides/css-and-tailwind.mdx
+++ b/docs/src/content/docs/guides/css-and-tailwind.mdx
@@ -10,12 +10,13 @@ You can style your Starlight site with custom CSS files or use the Starlight Tai
Customize the styles applied to your Starlight site by providing additional CSS files to modify or extend Starlight’s default styles.
1. Add a CSS file to your `src/` directory.
- For example, you could override Starlight’s default blue accent hue to purple:
+ For example, you could set a wider default column width and larger text size for page titles:
```css
/* src/styles/custom.css */
:root {
- --sl-hue-accent: 270;
+ --sl-content-width: 50rem;
+ --sl-text-5xl: 3.5rem;
}
```
@@ -231,3 +232,57 @@ module.exports = {
plugins: [starlightPlugin()],
};
```
+
+## Theming
+
+Starlight’s color theme can be controlled by overriding its default custom properties.
+These variables are used throughout the UI with a range of gray shades used for text and background colors and an accent color used for links and to highlight current items in navigation.
+
+### Color theme editor
+
+Use the sliders below to modify Starlight’s accent and gray color palettes.
+The dark and light preview areas will show the resulting colors, and the whole page will also update to preview your changes.
+
+When you’re happy with your changes, copy the CSS or Tailwind code below and use it in your project.
+
+import ThemeDesigner from '../../../components/theme-designer.astro';
+
+
+
+ Add the following CSS to your project in a [custom CSS
+ file](#custom-css-styles) to apply this theme to your site.
+
+
+ The example [Tailwind config file](#styling-starlight-with-tailwind) below
+ includes generated `accent` and `gray` color palettes to use in the
+ `theme.extend.colors` configuration object.
+
+
diff --git a/packages/starlight/components/Icons.ts b/packages/starlight/components/Icons.ts
index 7cf21b4b..92d5cc61 100644
--- a/packages/starlight/components/Icons.ts
+++ b/packages/starlight/components/Icons.ts
@@ -52,6 +52,8 @@ export const Icons = {
'',
'list-format':
'',
+ random:
+ '',
github:
'',
gitlab:
diff --git a/packages/starlight/style/props.css b/packages/starlight/style/props.css
index aaba636c..fd04d7ec 100644
--- a/packages/starlight/style/props.css
+++ b/packages/starlight/style/props.css
@@ -2,13 +2,13 @@
::backdrop {
/* Colors (dark mode) */
--sl-color-white: hsl(0, 0%, 100%); /* “white” */
- --sl-color-gray-1: hsl(231, 23%, 94%);
- --sl-color-gray-2: hsl(228, 8%, 77%);
- --sl-color-gray-3: hsl(221, 8%, 56%);
- --sl-color-gray-4: hsl(225, 9%, 36%);
- --sl-color-gray-5: hsl(222, 11%, 23%);
- --sl-color-gray-6: hsl(222, 13%, 16%);
- --sl-color-black: hsl(223, 13%, 10%);
+ --sl-color-gray-1: hsl(224, 20%, 94%);
+ --sl-color-gray-2: hsl(224, 6%, 77%);
+ --sl-color-gray-3: hsl(224, 6%, 56%);
+ --sl-color-gray-4: hsl(224, 7%, 36%);
+ --sl-color-gray-5: hsl(224, 10%, 23%);
+ --sl-color-gray-6: hsl(224, 14%, 16%);
+ --sl-color-black: hsl(224, 10%, 10%);
--sl-hue-orange: 41;
--sl-color-orange-low: hsl(var(--sl-hue-orange), 39%, 22%);
@@ -31,10 +31,9 @@
--sl-color-red: hsl(var(--sl-hue-red), 82%, 63%);
--sl-color-red-high: hsl(var(--sl-hue-red), 82%, 87%);
- --sl-hue-accent: var(--sl-hue-blue);
- --sl-color-accent-low: hsl(var(--sl-hue-accent), 54%, 20%);
- --sl-color-accent: hsl(var(--sl-hue-accent), 100%, 60%);
- --sl-color-accent-high: hsl(var(--sl-hue-accent), 100%, 87%);
+ --sl-color-accent-low: hsl(224, 54%, 20%);
+ --sl-color-accent: hsl(224, 100%, 60%);
+ --sl-color-accent-high: hsl(224, 100%, 85%);
--sl-color-text: var(--sl-color-gray-2);
--sl-color-text-accent: var(--sl-color-accent-high);
@@ -113,14 +112,14 @@
:root[data-theme='light'],
[data-theme='light'] ::backdrop {
/* Colours (light mode) */
- --sl-color-white: hsl(223, 13%, 10%);
- --sl-color-gray-1: hsl(222, 13%, 16%);
- --sl-color-gray-2: hsl(222, 11%, 23%);
- --sl-color-gray-3: hsl(225, 9%, 36%);
- --sl-color-gray-4: hsl(221, 8%, 56%);
- --sl-color-gray-5: hsl(228, 8%, 77%);
- --sl-color-gray-6: hsl(231, 23%, 94%);
- --sl-color-gray-7: hsl(240, 20%, 97%);
+ --sl-color-white: hsl(224, 10%, 10%);
+ --sl-color-gray-1: hsl(224, 14%, 16%);
+ --sl-color-gray-2: hsl(224, 10%, 23%);
+ --sl-color-gray-3: hsl(224, 7%, 36%);
+ --sl-color-gray-4: hsl(224, 6%, 56%);
+ --sl-color-gray-5: hsl(224, 6%, 77%);
+ --sl-color-gray-6: hsl(224, 20%, 94%);
+ --sl-color-gray-7: hsl(224, 19%, 97%);
--sl-color-black: hsl(0, 0%, 100%);
--sl-color-orange-high: hsl(var(--sl-hue-orange), 80%, 25%);
@@ -139,9 +138,9 @@
--sl-color-red: hsl(var(--sl-hue-red), 90%, 60%);
--sl-color-red-low: hsl(var(--sl-hue-red), 80%, 90%);
- --sl-color-accent-high: hsl(var(--sl-hue-accent), 80%, 30%);
- --sl-color-accent: hsl(var(--sl-hue-accent), 90%, 60%);
- --sl-color-accent-low: hsl(var(--sl-hue-accent), 88%, 90%);
+ --sl-color-accent-high: hsl(234, 80%, 30%);
+ --sl-color-accent: hsl(234, 90%, 60%);
+ --sl-color-accent-low: hsl(234, 88%, 90%);
--sl-color-text-accent: var(--sl-color-accent);
--sl-color-text-invert: var(--sl-color-black);
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index 05ceabf7..649f165c 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -35,9 +35,15 @@ importers:
'@astrojs/starlight':
specifier: workspace:*
version: link:../packages/starlight
+ '@types/culori':
+ specifier: ^2.0.0
+ version: 2.0.0
astro:
specifier: ^2.10.4
version: 2.10.4(sharp@0.32.3)
+ culori:
+ specifier: ^3.2.0
+ version: 3.2.0
sharp:
specifier: ^0.32.3
version: 0.32.3
@@ -1396,6 +1402,10 @@ packages:
resolution: {integrity: sha512-mEo1sAde+UCE6b2hxn332f1g1E8WfYRu6p5SvTKr2ZKC1f7gFJXk4h5PyGP9Dt6gCaG8y8XhwnXWC6Iy2cmBng==}
dev: true
+ /@types/culori@2.0.0:
+ resolution: {integrity: sha512-bKpEra39sQS9UZ+1JoWhuGJEzwKS0dUkNCohVYmn6CAEBkqyIXimKiPDRZWtiOB7sKgkWMaTUpHFimygRoGIlg==}
+ dev: false
+
/@types/debug@4.1.7:
resolution: {integrity: sha512-9AonUzyTjXXhEOa0DnqpzZi6VHlqKMswga9EXjpXnnqxwLtdvPPtlO8evrI5D9S6asFRCQ6v+wpiUKbw+vKqyg==}
dependencies:
@@ -2374,6 +2384,11 @@ packages:
stream-transform: 2.1.3
dev: true
+ /culori@3.2.0:
+ resolution: {integrity: sha512-HIEbTSP7vs1mPq/2P9In6QyFE0Tkpevh0k9a+FkjhD+cwsYm9WRSbn4uMdW9O0yXlNYC3ppxL3gWWPOcvEl57w==}
+ engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0}
+ dev: false
+
/data-uri-to-buffer@4.0.1:
resolution: {integrity: sha512-0R9ikRb668HB7QDxT1vkpuUBtqc53YyAwMwGeUFKRojY/NWKvdZ+9UYtRfGmhqNbRkTSVpMbmyhXipFFv2cb/A==}
engines: {node: '>= 12'}
--
cgit