feat(color): support custom colors
This commit is contained in:
parent
cde237f706
commit
b1e2927570
23
README.md
23
README.md
|
@ -12,6 +12,7 @@ Inspired by [jsonresume-theme-flat](https://github.com/erming/jsonresume-theme-f
|
||||||
- 💄 Markdown support
|
- 💄 Markdown support
|
||||||
- 📐 CSS grid layout
|
- 📐 CSS grid layout
|
||||||
- 🌗 Light and dark modes
|
- 🌗 Light and dark modes
|
||||||
|
- 🎨 Customizable colors
|
||||||
- 🧩 Standalone CLI
|
- 🧩 Standalone CLI
|
||||||
- 📦 ESM and CommonJS builds
|
- 📦 ESM and CommonJS builds
|
||||||
|
|
||||||
|
@ -50,3 +51,25 @@ _Even_ comes with a barebones CLI that reads resumes from `stdin` and outputs HT
|
||||||
```console
|
```console
|
||||||
npx jsonresume-theme-even < resume.json > resume.html
|
npx jsonresume-theme-even < resume.json > resume.html
|
||||||
```
|
```
|
||||||
|
|
||||||
|
## Options
|
||||||
|
|
||||||
|
### Colors
|
||||||
|
|
||||||
|
You can override theme colors via the `.meta.colors` resume field. Each entry defines a tuple of light and (optional) dark color values. If only one array value is defined, it will be used in both light and dark modes.
|
||||||
|
|
||||||
|
Here's an example using the default theme colors:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"meta": {
|
||||||
|
"colors": {
|
||||||
|
"background": ["#ffffff", "#191e23"],
|
||||||
|
"dimmed": ["#f3f4f5", "#23282d"],
|
||||||
|
"primary": ["#191e23", "#fbfbfc"],
|
||||||
|
"secondary": ["#6c7781", "#ccd0d4"],
|
||||||
|
"accent": ["#0073aa", "#00a0d2"]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
|
@ -11,11 +11,12 @@ import References from './components/references.js'
|
||||||
import Skills from './components/skills.js'
|
import Skills from './components/skills.js'
|
||||||
import Volunteer from './components/volunteer.js'
|
import Volunteer from './components/volunteer.js'
|
||||||
import Work from './components/work.js'
|
import Work from './components/work.js'
|
||||||
|
import colors from './utils/colors.js'
|
||||||
import html from './utils/html.js'
|
import html from './utils/html.js'
|
||||||
|
|
||||||
export default function Resume(resume, css) {
|
export default function Resume(resume, css) {
|
||||||
return html`<!DOCTYPE html>
|
return html`<!DOCTYPE html>
|
||||||
<html lang="en">
|
<html lang="en" style="${colors(resume.meta)}">
|
||||||
<head>
|
<head>
|
||||||
<meta charset="utf-8">
|
<meta charset="utf-8">
|
||||||
${Meta(resume.basics)}
|
${Meta(resume.basics)}
|
||||||
|
|
38
style.css
38
style.css
|
@ -1,11 +1,23 @@
|
||||||
:root {
|
:root {
|
||||||
color-scheme: light dark;
|
color-scheme: light dark;
|
||||||
|
|
||||||
--color-background: #ffffff; /* White */
|
--color-background-light: #ffffff; /* White */
|
||||||
--color-muted: #f3f4f5; /* Light Gray 200 */
|
--color-dimmed-light: #f3f4f5; /* Light Gray 200 */
|
||||||
--color-primary: #191e23; /* Dark Gray 900 */
|
--color-primary-light: #191e23; /* Dark Gray 900 */
|
||||||
--color-secondary: #6c7781; /* Dark Gray 300 */
|
--color-secondary-light: #6c7781; /* Dark Gray 300 */
|
||||||
--color-accent: #0073aa; /* WordPress Blue */
|
--color-accent-light: #0073aa; /* WordPress Blue */
|
||||||
|
|
||||||
|
--color-background-dark: #191e23; /* Dark Gray 900 */
|
||||||
|
--color-dimmed-dark: #23282d; /* Dark Gray 800 */
|
||||||
|
--color-primary-dark: #fbfbfc; /* Light Gray 100 */
|
||||||
|
--color-secondary-dark: #ccd0d4; /* Light Gray 700 */
|
||||||
|
--color-accent-dark: #00a0d2; /* Medium Blue */
|
||||||
|
|
||||||
|
--color-background: var(--color-background-light);
|
||||||
|
--color-dimmed: var(--color-dimmed-light);
|
||||||
|
--color-primary: var(--color-primary-light);
|
||||||
|
--color-secondary: var(--color-secondary-light);
|
||||||
|
--color-accent: var(--color-accent-light);
|
||||||
|
|
||||||
--scale-ratio: 1.25;
|
--scale-ratio: 1.25;
|
||||||
--scale0: 1rem;
|
--scale0: 1rem;
|
||||||
|
@ -18,11 +30,11 @@
|
||||||
|
|
||||||
@media (prefers-color-scheme: dark) {
|
@media (prefers-color-scheme: dark) {
|
||||||
:root {
|
:root {
|
||||||
--color-background: #191e23; /* Dark Gray 900 */
|
--color-background: var(--color-background-dark);
|
||||||
--color-muted: #23282d; /* Dark Gray 800 */
|
--color-dimmed: var(--color-dimmed-dark);
|
||||||
--color-primary: #fbfbfc; /* Light Gray 100 */
|
--color-primary: var(--color-primary-dark);
|
||||||
--color-secondary: #ccd0d4; /* Light Gray 700 */
|
--color-secondary: var(--color-secondary-dark);
|
||||||
--color-accent: #00a0d2; /* Medium Blue */
|
--color-accent: var(--color-accent-dark);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -128,7 +140,7 @@ h6 {
|
||||||
}
|
}
|
||||||
|
|
||||||
blockquote {
|
blockquote {
|
||||||
border-left: 0.2em solid var(--color-muted);
|
border-left: 0.2em solid var(--color-dimmed);
|
||||||
padding-left: 1em;
|
padding-left: 1em;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -147,7 +159,7 @@ svg {
|
||||||
}
|
}
|
||||||
|
|
||||||
.masthead {
|
.masthead {
|
||||||
background: var(--color-muted);
|
background: var(--color-dimmed);
|
||||||
display: inherit;
|
display: inherit;
|
||||||
gap: inherit;
|
gap: inherit;
|
||||||
grid-column: full;
|
grid-column: full;
|
||||||
|
@ -206,7 +218,7 @@ blockquote > * + *,
|
||||||
}
|
}
|
||||||
|
|
||||||
.tag-list > li {
|
.tag-list > li {
|
||||||
background: var(--color-muted);
|
background: var(--color-dimmed);
|
||||||
border-radius: 0.2em;
|
border-radius: 0.2em;
|
||||||
padding: 0.2em 0.6em;
|
padding: 0.2em 0.6em;
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
|
|
||||||
exports[`renders a resume 1`] = `
|
exports[`renders a resume 1`] = `
|
||||||
"<!DOCTYPE html>
|
"<!DOCTYPE html>
|
||||||
<html lang=\\"en\\">
|
<html lang=\\"en\\" style=\\"--color-background-light:lightgray; --color-background-dark:darkgray;\\">
|
||||||
<head>
|
<head>
|
||||||
<meta charset=\\"utf-8\\">
|
<meta charset=\\"utf-8\\">
|
||||||
|
|
||||||
|
|
|
@ -2,10 +2,21 @@ import { HtmlValidate } from 'html-validate'
|
||||||
import { expect, it } from 'vitest'
|
import { expect, it } from 'vitest'
|
||||||
|
|
||||||
import { render } from '../index.js'
|
import { render } from '../index.js'
|
||||||
import resume from 'resume-schema/sample.resume.json' assert { type: 'json' }
|
import sampleResume from 'resume-schema/sample.resume.json' assert { type: 'json' }
|
||||||
|
|
||||||
// Overwrite empty sample resume values
|
const resume = {
|
||||||
resume.basics.image = 'image.jpg'
|
...sampleResume,
|
||||||
|
meta: {
|
||||||
|
...sampleResume.meta,
|
||||||
|
colors: {
|
||||||
|
background: ['lightgray', 'darkgray'],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
basics: {
|
||||||
|
...sampleResume.basics,
|
||||||
|
image: 'image.jpg',
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
it('renders a resume', () => {
|
it('renders a resume', () => {
|
||||||
expect(render(resume)).toMatchSnapshot()
|
expect(render(resume)).toMatchSnapshot()
|
||||||
|
@ -15,6 +26,7 @@ it('renders valid HTML', async () => {
|
||||||
const htmlvalidate = new HtmlValidate({
|
const htmlvalidate = new HtmlValidate({
|
||||||
extends: ['html-validate:recommended'],
|
extends: ['html-validate:recommended'],
|
||||||
rules: {
|
rules: {
|
||||||
|
'no-inline-style': 'off',
|
||||||
'no-trailing-whitespace': 'off',
|
'no-trailing-whitespace': 'off',
|
||||||
'tel-non-breaking': 'off',
|
'tel-non-breaking': 'off',
|
||||||
},
|
},
|
||||||
|
|
|
@ -0,0 +1,6 @@
|
||||||
|
export default function colors(meta = {}) {
|
||||||
|
const { colors } = meta
|
||||||
|
return colors && Object.entries(colors)
|
||||||
|
.map(([name, [light, dark = light]]) => `--color-${name}-light:${light}; --color-${name}-dark:${dark};`)
|
||||||
|
.join(' ')
|
||||||
|
}
|
Loading…
Reference in New Issue