colorsbymax 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 +38 -0
- package/README.md +199 -138
- package/THIRD_PARTY_NOTICES.md +47 -1
- package/dist/ThemeSwitcher-CnxUWdBP.js +4673 -0
- package/dist/auto.js +48 -0
- package/dist/index.js +1 -4201
- package/dist/pdf.js +9 -0
- package/package.json +51 -11
- package/types/auto.d.ts +10 -0
- package/types/index.d.ts +285 -0
- package/types/pdf.d.ts +7 -0
package/dist/pdf.js
ADDED
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
//#region src/pdf.js
|
|
2
|
+
/** Loads PDF.js, running it on the page so there's no separate worker file to host. */
|
|
3
|
+
async function loadPdf() {
|
|
4
|
+
const pdfjs = await import("pdfjs-dist");
|
|
5
|
+
await import("pdfjs-dist/build/pdf.worker.min.mjs");
|
|
6
|
+
return pdfjs;
|
|
7
|
+
}
|
|
8
|
+
//#endregion
|
|
9
|
+
export { loadPdf };
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "colorsbymax",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"description": "
|
|
3
|
+
"version": "0.2.0",
|
|
4
|
+
"description": "A live theme switcher for any website: site themes, 700+ palettes, for light/dark modes and WCAG contrast checks.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"theme",
|
|
7
7
|
"theme-switcher",
|
|
@@ -25,47 +25,87 @@
|
|
|
25
25
|
"url": "https://github.com/MaxMuyalwa/colorsbymax/issues"
|
|
26
26
|
},
|
|
27
27
|
"type": "module",
|
|
28
|
-
"main": "dist/index.js",
|
|
29
|
-
"module": "dist/index.js",
|
|
28
|
+
"main": "./dist/index.js",
|
|
29
|
+
"module": "./dist/index.js",
|
|
30
|
+
"types": "./types/index.d.ts",
|
|
31
|
+
"typesVersions": {
|
|
32
|
+
"*": {
|
|
33
|
+
"pdf": [
|
|
34
|
+
"./types/pdf.d.ts"
|
|
35
|
+
],
|
|
36
|
+
"auto": [
|
|
37
|
+
"./types/auto.d.ts"
|
|
38
|
+
]
|
|
39
|
+
}
|
|
40
|
+
},
|
|
30
41
|
"exports": {
|
|
31
42
|
".": {
|
|
43
|
+
"types": "./types/index.d.ts",
|
|
32
44
|
"import": "./dist/index.js",
|
|
33
45
|
"default": "./dist/index.js"
|
|
34
46
|
},
|
|
47
|
+
"./auto": {
|
|
48
|
+
"types": "./types/auto.d.ts",
|
|
49
|
+
"import": "./dist/auto.js",
|
|
50
|
+
"default": "./dist/auto.js"
|
|
51
|
+
},
|
|
52
|
+
"./pdf": {
|
|
53
|
+
"types": "./types/pdf.d.ts",
|
|
54
|
+
"import": "./dist/pdf.js",
|
|
55
|
+
"default": "./dist/pdf.js"
|
|
56
|
+
},
|
|
35
57
|
"./tokens.css": "./src/tokens.css",
|
|
36
58
|
"./package.json": "./package.json"
|
|
37
59
|
},
|
|
38
|
-
"sideEffects":
|
|
60
|
+
"sideEffects": [
|
|
61
|
+
"./dist/auto.js"
|
|
62
|
+
],
|
|
63
|
+
"engines": {
|
|
64
|
+
"node": ">=18"
|
|
65
|
+
},
|
|
39
66
|
"files": [
|
|
40
67
|
"dist",
|
|
68
|
+
"types/index.d.ts",
|
|
69
|
+
"types/auto.d.ts",
|
|
70
|
+
"types/pdf.d.ts",
|
|
41
71
|
"src/tokens.css",
|
|
42
72
|
"LICENSE",
|
|
43
|
-
"THIRD_PARTY_NOTICES.md"
|
|
73
|
+
"THIRD_PARTY_NOTICES.md",
|
|
74
|
+
"CHANGELOG.md"
|
|
44
75
|
],
|
|
45
76
|
"scripts": {
|
|
46
77
|
"dev": "vite",
|
|
47
78
|
"build": "node scripts/build-css.mjs && vite build --config vite.lib.config.js",
|
|
48
|
-
"
|
|
79
|
+
"typecheck": "tsc -p types && node scripts/check-types.mjs",
|
|
80
|
+
"prepublishOnly": "npm run build && npm run typecheck",
|
|
49
81
|
"build:demo": "vite build",
|
|
50
82
|
"css": "node scripts/build-css.mjs",
|
|
83
|
+
"icons": "node scripts/build-icons.mjs",
|
|
51
84
|
"presets": "node scripts/generate-presets.mjs"
|
|
52
85
|
},
|
|
53
86
|
"peerDependencies": {
|
|
54
87
|
"react": "^18.2.0 || ^19.0.0",
|
|
55
|
-
"react-dom": "^18.2.0 || ^19.0.0"
|
|
88
|
+
"react-dom": "^18.2.0 || ^19.0.0",
|
|
89
|
+
"pdfjs-dist": ">=4"
|
|
56
90
|
},
|
|
57
|
-
"
|
|
58
|
-
"
|
|
59
|
-
|
|
91
|
+
"peerDependenciesMeta": {
|
|
92
|
+
"pdfjs-dist": {
|
|
93
|
+
"optional": true
|
|
94
|
+
}
|
|
60
95
|
},
|
|
61
96
|
"devDependencies": {
|
|
62
97
|
"@tailwindcss/cli": "^4.3.3",
|
|
63
98
|
"@tailwindcss/vite": "^4.3.3",
|
|
99
|
+
"@types/react": "^19.3.0",
|
|
100
|
+
"@types/react-dom": "^19.3.0",
|
|
64
101
|
"@vitejs/plugin-react": "^6.1.1",
|
|
102
|
+
"lucide-react": "^1.48.0",
|
|
65
103
|
"nice-color-palettes": "^4.0.0",
|
|
104
|
+
"pdfjs-dist": "^6.3.289",
|
|
66
105
|
"react": "^19.3.0",
|
|
67
106
|
"react-dom": "^19.3.0",
|
|
68
107
|
"tailwindcss": "^4.3.3",
|
|
108
|
+
"typescript": "^5.9.3",
|
|
69
109
|
"vite": "^8.3.1"
|
|
70
110
|
},
|
|
71
111
|
"license": "MIT"
|
package/types/auto.d.ts
ADDED
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
// Type declarations for 'colorsbymax/auto'. Importing the module mounts the colour button by
|
|
2
|
+
// itself once the page has rendered: `import 'colorsbymax/auto'`.
|
|
3
|
+
|
|
4
|
+
import type { ColorsByMaxConfig } from './index.js'
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Mounts the colour button and panel with settings, instead of the automatic mount. Calling it
|
|
8
|
+
* again replaces the previous mount. Returns a function that removes it.
|
|
9
|
+
*/
|
|
10
|
+
export function autoMount(config?: ColorsByMaxConfig): () => void
|
package/types/index.d.ts
ADDED
|
@@ -0,0 +1,285 @@
|
|
|
1
|
+
// Type declarations for colorsbymax. The package is written in JavaScript; these describe its
|
|
2
|
+
// public API and are checked against real usage by types/check.tsx (npm run typecheck).
|
|
3
|
+
|
|
4
|
+
import type { ReactElement, ReactNode } from 'react'
|
|
5
|
+
|
|
6
|
+
// ---------------------------------------------------------------- tokens and themes
|
|
7
|
+
|
|
8
|
+
/** Every themeable colour. Each is exposed to CSS as `--color-<key>`. */
|
|
9
|
+
export type TokenKey =
|
|
10
|
+
| 'primary' | 'primary-dark' | 'primary-alt' | 'on-primary'
|
|
11
|
+
| 'background' | 'surface' | 'secondary' | 'on-secondary' | 'accent' | 'on-accent' | 'border' | 'shadow'
|
|
12
|
+
| 'ink' | 'ink-secondary' | 'ink-muted'
|
|
13
|
+
| 'success' | 'warning' | 'danger' | 'info'
|
|
14
|
+
| 'data-1' | 'data-2' | 'data-3' | 'data-4' | 'data-5' | 'data-6' | 'data-7' | 'data-8'
|
|
15
|
+
| 'app-background' | 'app-input' | 'app-border' | 'app-primary' | 'app-shadow-dark' | 'app-shadow-light' | 'app-ink' | 'app-ink-muted'
|
|
16
|
+
|
|
17
|
+
/** Token key → `"#rrggbb"`. */
|
|
18
|
+
export type ThemeTokens = Record<TokenKey, string>
|
|
19
|
+
|
|
20
|
+
export interface Theme {
|
|
21
|
+
id: string
|
|
22
|
+
name: string
|
|
23
|
+
tokens: ThemeTokens
|
|
24
|
+
/** A palette the visitor created or imported. */
|
|
25
|
+
custom?: boolean
|
|
26
|
+
/** From the generated library. */
|
|
27
|
+
library?: boolean
|
|
28
|
+
/** One of the host site's own themes (including scan suggestions). */
|
|
29
|
+
site?: boolean
|
|
30
|
+
/** Suggested by a site scan. */
|
|
31
|
+
scanned?: boolean
|
|
32
|
+
/** A generated dark twin of a light theme. */
|
|
33
|
+
derived?: boolean
|
|
34
|
+
/** Library categories, for library themes. */
|
|
35
|
+
tags?: string[]
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
export interface TokenGroup {
|
|
39
|
+
group: string
|
|
40
|
+
tokens: { key: TokenKey; label: string; usage: string }[]
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
export const TOKEN_GROUPS: TokenGroup[]
|
|
44
|
+
export const TOKEN_KEYS: TokenKey[]
|
|
45
|
+
/** The neutral default theme's tokens (Slate). */
|
|
46
|
+
export const BASE_TOKENS: ThemeTokens
|
|
47
|
+
/** The hand-tuned themes shown as "Max's picks". */
|
|
48
|
+
export const PRESETS: Theme[]
|
|
49
|
+
|
|
50
|
+
/** Fills missing tokens from `base` (the neutral default unless given), deriving the secondary-area ones. */
|
|
51
|
+
export function completeTokens(partial: Partial<ThemeTokens>, base?: ThemeTokens): ThemeTokens
|
|
52
|
+
/** Derives the secondary-area (`app-*`) tokens from a theme's main palette. */
|
|
53
|
+
export function deriveAppTokens(tokens: ThemeTokens): Pick<ThemeTokens, Extract<TokenKey, `app-${string}`>>
|
|
54
|
+
|
|
55
|
+
// ---------------------------------------------------------------- React API
|
|
56
|
+
|
|
57
|
+
export interface ColorsByMaxConfig {
|
|
58
|
+
/** Name of the first theme group, e.g. "Tsungi". Detected from the page if left out. */
|
|
59
|
+
siteName?: string
|
|
60
|
+
/** localStorage key; must match the pre-paint script. Defaults to "colorsbymax". */
|
|
61
|
+
storageKey?: string
|
|
62
|
+
/** The site's own colours. Missing tokens are filled in. */
|
|
63
|
+
defaultTheme?: { name: string; tokens: Partial<ThemeTokens> }
|
|
64
|
+
/** Extra themes made for the site. */
|
|
65
|
+
themes?: { id: string; name: string; tokens: Partial<ThemeTokens> }[]
|
|
66
|
+
/** Where each token is used on the site, shown in the colour editors. */
|
|
67
|
+
usage?: Partial<Record<TokenKey, string>>
|
|
68
|
+
/** Colour the page's scrollbars from the theme. Default true. */
|
|
69
|
+
scrollbars?: boolean
|
|
70
|
+
/** Enables PDF uploads: pass `loadPdf` from 'colorsbymax/pdf' (needs pdfjs-dist installed). */
|
|
71
|
+
pdf?: () => Promise<unknown>
|
|
72
|
+
/**
|
|
73
|
+
* Re-colour a site that doesn't paint with the --color-* variables, by swapping the colours
|
|
74
|
+
* actually on the page. 'auto' (the default) does it only when the site doesn't define
|
|
75
|
+
* --color-primary itself; false never does.
|
|
76
|
+
*/
|
|
77
|
+
recolour?: boolean | 'auto'
|
|
78
|
+
/**
|
|
79
|
+
* Where the colour button starts. Default 'bottom-right', like chat and help widgets;
|
|
80
|
+
* 'top-right' sits just under a floating nav bar. Visitors can still drag it anywhere.
|
|
81
|
+
*/
|
|
82
|
+
position?: 'bottom-right' | 'bottom-left' | 'top-left' | 'top-right'
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
export interface ThemeProviderProps {
|
|
86
|
+
config?: ColorsByMaxConfig
|
|
87
|
+
children?: ReactNode
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
/** Applies the saved or chosen theme to the page and provides it to the switcher and useTheme(). */
|
|
91
|
+
export function ThemeProvider(props: ThemeProviderProps): ReactElement
|
|
92
|
+
|
|
93
|
+
/** The floating colour button and panel. Render it once, inside ThemeProvider. */
|
|
94
|
+
export function ThemeSwitcher(): ReactElement | null
|
|
95
|
+
|
|
96
|
+
export interface ScanResult {
|
|
97
|
+
at: number
|
|
98
|
+
palette: string[]
|
|
99
|
+
themes: Theme[]
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
export interface ThemeState {
|
|
103
|
+
activeId: string
|
|
104
|
+
overrides: Partial<ThemeTokens>
|
|
105
|
+
customs: Theme[]
|
|
106
|
+
snapshot: Pick<Theme, 'id' | 'name' | 'tokens'> | null
|
|
107
|
+
scanned: ScanResult | null
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
export interface ThemeApi {
|
|
111
|
+
state: ThemeState
|
|
112
|
+
storageKey: string
|
|
113
|
+
siteName: string
|
|
114
|
+
usage: Partial<Record<TokenKey, string>>
|
|
115
|
+
loadPdf: (() => Promise<unknown>) | null
|
|
116
|
+
position: 'bottom-right' | 'bottom-left' | 'top-left' | 'top-right'
|
|
117
|
+
/** True when colorsbymax is swapping the page's own colours (the site isn't using the variables). */
|
|
118
|
+
recolouring: boolean
|
|
119
|
+
/** The site's own themes, then any scan suggestions. */
|
|
120
|
+
siteThemes: Theme[]
|
|
121
|
+
defaultTheme: Theme
|
|
122
|
+
presets: Theme[]
|
|
123
|
+
customs: Theme[]
|
|
124
|
+
/** Site themes, presets and custom palettes (not the library). */
|
|
125
|
+
themes: Theme[]
|
|
126
|
+
/** The applied theme. */
|
|
127
|
+
active: Theme
|
|
128
|
+
/** The applied colours: the active theme plus any overrides. */
|
|
129
|
+
tokens: ThemeTokens
|
|
130
|
+
/** Contrast problems in the applied colours. */
|
|
131
|
+
issues: ContrastIssue[]
|
|
132
|
+
|
|
133
|
+
/** Selects a theme by id; pass the theme itself for library themes and dark twins. */
|
|
134
|
+
selectTheme(id: string, theme?: Theme): void
|
|
135
|
+
/** Creates a custom palette from the current colours and applies it. Returns its id. */
|
|
136
|
+
createCustom(name: string): string
|
|
137
|
+
updateCustomToken(id: string, key: TokenKey, value: string): void
|
|
138
|
+
renameCustom(id: string, name: string): void
|
|
139
|
+
deleteCustom(id: string): void
|
|
140
|
+
/** Adds tokens as a new custom palette and applies it. Returns its id. */
|
|
141
|
+
addPalette(name: string, tokens: Partial<ThemeTokens>): string
|
|
142
|
+
|
|
143
|
+
setOverride(key: TokenKey, value: string): void
|
|
144
|
+
clearOverride(key: TokenKey): void
|
|
145
|
+
clearOverrides(): void
|
|
146
|
+
resetToDefault(): void
|
|
147
|
+
|
|
148
|
+
scanned: ScanResult | null
|
|
149
|
+
/** Reads the page's colours and adds themes built around them. Resolves to counts for the UI. */
|
|
150
|
+
runScan(): Promise<{ colours: number; themes: number }>
|
|
151
|
+
clearScan(): void
|
|
152
|
+
|
|
153
|
+
/** Fixes one issue by adjusting lightness. Returns false if no single change could. */
|
|
154
|
+
fixIssue(issue: ContrastIssue): boolean
|
|
155
|
+
fixAllIssues(): void
|
|
156
|
+
|
|
157
|
+
/** The applied theme as `{ name, tokens }` JSON. */
|
|
158
|
+
exportTheme(): string
|
|
159
|
+
/** Imports `{ name, tokens }` JSON as a custom palette. Returns an error message, or null. */
|
|
160
|
+
importTheme(json: string): string | null
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
/** The theme state and actions. Must be called inside ThemeProvider. */
|
|
164
|
+
export function useTheme(): ThemeApi
|
|
165
|
+
|
|
166
|
+
/** Writes each token to `--color-<key>` on `<html>`. */
|
|
167
|
+
export function applyTokens(tokens: ThemeTokens): void
|
|
168
|
+
|
|
169
|
+
// ---------------------------------------------------------------- pre-paint and settings
|
|
170
|
+
|
|
171
|
+
export const DEFAULT_STORAGE_KEY: 'colorsbymax'
|
|
172
|
+
/** Source of an inline `<script>` for `<head>` that applies the saved theme before first paint. */
|
|
173
|
+
export function prePaintScript(storageKey?: string): string
|
|
174
|
+
|
|
175
|
+
export interface PanelSettings {
|
|
176
|
+
mode: 'light' | 'dark' | 'system'
|
|
177
|
+
showPicks: boolean
|
|
178
|
+
showLibrary: boolean
|
|
179
|
+
showCustom: boolean
|
|
180
|
+
showOverrides: boolean
|
|
181
|
+
showImportExport: boolean
|
|
182
|
+
draggable: boolean
|
|
183
|
+
animateDot: boolean
|
|
184
|
+
panelWidth: number | null
|
|
185
|
+
panelHeight: number | 'full' | null
|
|
186
|
+
libraryCollapsed: boolean
|
|
187
|
+
}
|
|
188
|
+
/** The visitor settings the panel starts with. */
|
|
189
|
+
export const DEFAULT_SETTINGS: PanelSettings
|
|
190
|
+
|
|
191
|
+
// ---------------------------------------------------------------- contrast
|
|
192
|
+
|
|
193
|
+
export type PairingKind = 'text' | 'large-text' | 'non-text'
|
|
194
|
+
|
|
195
|
+
export interface Pairing {
|
|
196
|
+
id: string
|
|
197
|
+
label: string
|
|
198
|
+
kind: PairingKind
|
|
199
|
+
fg(tokens: ThemeTokens): string
|
|
200
|
+
bg(tokens: ThemeTokens): string
|
|
201
|
+
/** Tokens auto-fix may adjust, in preference order. */
|
|
202
|
+
fixable: TokenKey[]
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
export interface ContrastIssue {
|
|
206
|
+
pairing: Pairing
|
|
207
|
+
ratio: number
|
|
208
|
+
required: number
|
|
209
|
+
/** e.g. "Primary text on background: 2.7:1 — text will be hard to read (needs 4.5:1)" */
|
|
210
|
+
message: string
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
/** Every foreground/background pairing colorsbymax checks. */
|
|
214
|
+
export const PAIRINGS: Pairing[]
|
|
215
|
+
export const MIN_CONTRAST_TEXT: number
|
|
216
|
+
export const MIN_CONTRAST_LARGE_TEXT: number
|
|
217
|
+
export const MIN_CONTRAST_NON_TEXT: number
|
|
218
|
+
export const MIN_RAMP_STEP_DELTA_E: number
|
|
219
|
+
|
|
220
|
+
/** Every failing pairing in a theme. */
|
|
221
|
+
export function checkTheme(tokens: ThemeTokens): ContrastIssue[]
|
|
222
|
+
/** The smallest lightness change to one token that makes a pairing pass, or null. */
|
|
223
|
+
export function suggestFix(pairing: Pairing, tokens: ThemeTokens): { key: TokenKey; value: string } | null
|
|
224
|
+
/** Fixes failing pairings until the theme passes or nothing more helps. Returns the changed tokens. */
|
|
225
|
+
export function fixAll(tokens: ThemeTokens): Partial<ThemeTokens>
|
|
226
|
+
/** Problems with an ordered colour ramp drawn on `surface`, as sentences. */
|
|
227
|
+
export function checkRamp(colors: string[], surface: string): string[]
|
|
228
|
+
|
|
229
|
+
// ---------------------------------------------------------------- colour helpers
|
|
230
|
+
|
|
231
|
+
/** WCAG 2 contrast ratio between two hex colours, 1–21. */
|
|
232
|
+
export function contrastRatio(a: string, b: string): number
|
|
233
|
+
/** "#abc", "abc" or "#AABBCC" → "#aabbcc"; null for anything else. */
|
|
234
|
+
export function normalizeHex(input: unknown): string | null
|
|
235
|
+
|
|
236
|
+
// ---------------------------------------------------------------- light and dark
|
|
237
|
+
|
|
238
|
+
/** True when a theme's page background is dark. */
|
|
239
|
+
export function isDarkTheme(tokens: ThemeTokens): boolean
|
|
240
|
+
/** Dark versions of a light theme's tokens, adjusted to pass contrast. */
|
|
241
|
+
export function darkTokens(tokens: ThemeTokens): ThemeTokens
|
|
242
|
+
/** The dark twin of a light theme (themes that are already dark come back unchanged). */
|
|
243
|
+
export function toDark<T extends Theme>(theme: T): T
|
|
244
|
+
|
|
245
|
+
// ---------------------------------------------------------------- site scan
|
|
246
|
+
|
|
247
|
+
export interface ColourTally {
|
|
248
|
+
hex: string
|
|
249
|
+
/** Area painted as a background. */
|
|
250
|
+
bg: number
|
|
251
|
+
/** Weighted amount of text in this colour. */
|
|
252
|
+
text: number
|
|
253
|
+
/** Border length in this colour. */
|
|
254
|
+
border: number
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
export type Roles = Partial<Record<TokenKey, string>>
|
|
258
|
+
|
|
259
|
+
/** The page's painted colours, ignoring any applied theme. `exclude` is a selector to skip. */
|
|
260
|
+
export function collectColors(options?: { exclude?: string }): ColourTally[]
|
|
261
|
+
/** Works out token roles from collected colours. */
|
|
262
|
+
export function inferRoles(colors: ColourTally[]): { roles: Roles; palette: string[] }
|
|
263
|
+
/** A complete theme from detected roles, deriving whatever wasn't found. */
|
|
264
|
+
export function themeFromRoles(
|
|
265
|
+
roles: Roles,
|
|
266
|
+
variant?: { softness?: number; boldness?: number; complementary?: boolean },
|
|
267
|
+
): ThemeTokens
|
|
268
|
+
/** Themes suggested for a scanned site, named after it, plus the closest library themes. */
|
|
269
|
+
export function suggestThemes(scan: { roles: Roles }, siteName: string, library?: Theme[]): Theme[]
|
|
270
|
+
/** Best guess at the site's name: og:site_name, then the title, then the host. */
|
|
271
|
+
export function detectSiteName(doc?: Document): string
|
|
272
|
+
|
|
273
|
+
// ---------------------------------------------------------------- palettes from files
|
|
274
|
+
|
|
275
|
+
/** The main colours in an image (or a PDF, with `loadPdf`), most important first. */
|
|
276
|
+
export function coloursFromFile(
|
|
277
|
+
file: File,
|
|
278
|
+
options?: { loadPdf?: (() => Promise<unknown>) | null },
|
|
279
|
+
): Promise<{ colours: string[]; from: 'image' | 'pdf-text' | 'pdf' }>
|
|
280
|
+
/** The dominant colours in sets of RGBA pixels, edge blends removed. */
|
|
281
|
+
export function dominantColours(pixelSets: Uint8ClampedArray[]): string[]
|
|
282
|
+
/** Assigns a palette's colours to theme roles. */
|
|
283
|
+
export function rolesFromPalette(colours: string[]): Roles
|
|
284
|
+
/** A complete, contrast-checked theme built around a palette. */
|
|
285
|
+
export function themeFromPalette(colours: string[]): ThemeTokens
|
package/types/pdf.d.ts
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
// Type declarations for 'colorsbymax/pdf'.
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Loads PDF.js for PDF uploads. Pass it as `pdf` in the ThemeProvider config; needs the
|
|
5
|
+
* pdfjs-dist package installed. PDF.js downloads only when a visitor picks a PDF.
|
|
6
|
+
*/
|
|
7
|
+
export function loadPdf(): Promise<unknown>
|