diff --git a/index.html b/index.html
index f5ae6b8..83afc25 100644
--- a/index.html
+++ b/index.html
@@ -16,6 +16,12 @@
+
diff --git a/src/editor/index.html b/src/editor/index.html
index 0a4d305..50ab87a 100644
--- a/src/editor/index.html
+++ b/src/editor/index.html
@@ -19,6 +19,13 @@
+
diff --git a/src/editor/main.js b/src/editor/main.js
index 20e70ed..02511c1 100644
--- a/src/editor/main.js
+++ b/src/editor/main.js
@@ -10,7 +10,9 @@ import * as sampleModule from '../../sample.cv.json';
import * as jsoncvSchemaModule from '../../schema/jsoncv.schema.json';
import {
getCVData,
+ getPrimaryColor,
saveCVJSON,
+ savePrimaryColor,
} from '../lib/store';
import {
createElement,
@@ -140,6 +142,7 @@ function getEditorData() {
const $outputJSON = $('.output-json')
const $outputHTML = $('.output-html')
+const outputHTMLIframe = $outputHTML.get(0)
// listen to change
editor.on('change', () => {
@@ -160,11 +163,13 @@ const $btnDownloadJSON = $('#fn-download-json')
const $btnDownloadHTML = $('#fn-download-html')
const $btnLoadSample = $('#fn-load-sample')
const $btnPrintPreview = $('#fn-print-preview')
+const $inputColorPicker = $('#fn-color-picker')
+const $colorValue = $('.color-picker .value')
const isElementHidden = elt =>
! (elt.offsetWidth || elt.offsetHeight || elt.getClientRects().length);
$btnTogglePreview.on('click', () => {
- if (isElementHidden($outputHTML.get(0))) {
+ if (isElementHidden(outputHTMLIframe)) {
$outputJSON.hide()
$outputHTML.show()
} else {
@@ -220,7 +225,7 @@ function downloadCV(contentType) {
downloadContent(filename, JSON.stringify(data, null, 2))
} else if (contentType === 'html') {
let filename = `${title}.html`
- downloadIframeHTML(filename, $outputHTML.get(0))
+ downloadIframeHTML(filename, outputHTMLIframe)
}
// update editor value
@@ -242,5 +247,19 @@ $btnLoadSample.on('click', () => {
})
$btnPrintPreview.on('click', () => {
- $outputHTML.get(0).contentWindow.print()
+ outputHTMLIframe.contentWindow.print()
})
+
+
+// primary color
+
+$inputColorPicker.on('change', (e) => {
+ const color = e.target.value
+ console.log('color', color)
+ $colorValue.text(color)
+ savePrimaryColor(color)
+})
+
+const primaryColor = getPrimaryColor()
+$colorValue.text(primaryColor)
+$inputColorPicker.val(primaryColor)
diff --git a/src/lib/store.js b/src/lib/store.js
index 82482b9..90a7c0f 100644
--- a/src/lib/store.js
+++ b/src/lib/store.js
@@ -1,11 +1,18 @@
export const storeKeys = {
cvJSON: 'cvJSON',
cvSavedTime: 'cvSavedTime',
+ primaryColor: 'primary-color',
+}
+
+const defaultPrimaryColor = '#2A3FFB'
+
+function updateSavedTime() {
+ localStorage.setItem(storeKeys.cvSavedTime, Date.now())
}
export function saveCVJSON(str) {
localStorage.setItem(storeKeys.cvJSON, str)
- localStorage.setItem(storeKeys.cvSavedTime, Date.now())
+ updateSavedTime()
}
export function getCVData() {
@@ -17,3 +24,12 @@ export function getCVData() {
export function getCVSavedTime() {
return localStorage.getItem(storeKeys.cvSavedTime)
}
+
+export function savePrimaryColor(color) {
+ localStorage.setItem(storeKeys.primaryColor, color)
+ updateSavedTime()
+}
+
+export function getPrimaryColor() {
+ return localStorage.getItem(storeKeys.primaryColor) || defaultPrimaryColor
+}
diff --git a/src/preview/main.js b/src/preview/main.js
index d986b9b..86b85c3 100644
--- a/src/preview/main.js
+++ b/src/preview/main.js
@@ -1,6 +1,7 @@
import {
getCVData,
getCVSavedTime,
+ getPrimaryColor,
} from '../lib/store';
import { renderThemeOn } from '../themes';
import { getCVTitle } from '../themes/data';
@@ -33,7 +34,7 @@ const restoreScrollPosition = () => {
// Render CV
const data = getCVData()
if (data) {
- renderThemeOn(themeName, elCV, data)
+ renderThemeOn(themeName, elCV, data, getPrimaryColor())
// change document title
document.title = getCVTitle(data)
// restore scroll position
diff --git a/src/scss/editor/index.scss b/src/scss/editor/index.scss
index 46e532f..f70ee77 100644
--- a/src/scss/editor/index.scss
+++ b/src/scss/editor/index.scss
@@ -1,4 +1,5 @@
@use '../basic';
+@use '../vars';
@use 'json-editor';
button {
@@ -72,9 +73,42 @@ button {
}
.app-actions {
- button {
+ > * {
margin-bottom: 8px;
}
+
+ .color-picker {
+ @include vars.button-base;
+ border: 1px solid var(--grey-2);
+ border-radius: 2px;
+ padding: 3px 8px;
+ font-size: 13px;
+ display: block;
+ width: 85px;
+
+ &:active {
+ border-color: #555;
+ }
+
+ .color-area {
+ display: flex;
+ align-items: center;
+ gap: 4px;
+ }
+
+ input[type=color] {
+ background: transparent;
+ padding: 0;
+ border: 0;
+ outline: 0;
+ width: 20px;
+ height: 24px;
+ @supports (-moz-appearance:none) {
+ width: 16px;
+ height: 16px;
+ }
+ }
+ }
}
.output-html {
diff --git a/src/themes/data.js b/src/themes/data.js
index d91d136..3f27dbd 100644
--- a/src/themes/data.js
+++ b/src/themes/data.js
@@ -2,6 +2,8 @@ import { reformatDate } from '../lib/date';
import { getIconSVG } from '../lib/icons';
import { renderMarkdown } from '../lib/markdown';
+export const primaryColorVarName = '--color-primary'
+
export function getRenderData(cvData) {
return {
cv: cvData,
diff --git a/src/themes/index.js b/src/themes/index.js
index 8ed124c..d61cace 100644
--- a/src/themes/index.js
+++ b/src/themes/index.js
@@ -1,6 +1,9 @@
import ejs from 'ejs';
-import { getRenderData } from './data';
+import {
+ getRenderData,
+ primaryColorVarName,
+} from './data';
const themes = {}
@@ -36,7 +39,7 @@ export function renderTheme(template, cvData, options) {
const cvStyleId = 'cv-style'
-export function renderThemeOn(name, el, data) {
+export function renderThemeOn(name, el, data, primaryColor) {
const theme = getTheme(name)
el.innerHTML = renderTheme(theme.template, data)
@@ -46,4 +49,6 @@ export function renderThemeOn(name, el, data) {
document.head.appendChild(elStyle)
}
elStyle.innerHTML = theme.style
+
+ document.documentElement.style.setProperty(primaryColorVarName, primaryColor)
}
diff --git a/src/themes/reorx/index.scss b/src/themes/reorx/index.scss
index 54cb0ea..01cc555 100644
--- a/src/themes/reorx/index.scss
+++ b/src/themes/reorx/index.scss
@@ -1,6 +1,5 @@
/* Naming convention: https://ricostacruz.com/rscss/ */
-$color-signature: #2A3FFB;
$color-text-dim: #777;
$color-text-dimmer: #999;
$color-border-dim: #aaa;
@@ -11,6 +10,11 @@ $fz-3: 18px;
$fz-4: 16px;
$lh-p: 1.4;
+:root {
+ // overide `--color-primary` in your own css
+ --color-primary: #aaa;
+}
+
.cv-container {
font-size: 14px;
font-family: system-ui, sans-serif;
@@ -31,11 +35,11 @@ $lh-p: 1.4;
}
a, a:visited, a:active {
- color: $color-signature;
+ color: var(--color-primary);
text-decoration: none;
}
a:hover {
- color: $color-signature;
+ color: var(--color-primary);
text-decoration: underline;
}
@@ -47,14 +51,14 @@ section {
h2 {
font-size: $fz-2;
font-weight: 600;
- color: $color-signature;
+ color: var(--color-primary);
margin: 0;
}
.line {
flex-grow: 1;
margin: 14px 0 0 1em;
height: 2px;
- background-color: $color-signature;
+ background-color: var(--color-primary);
}
}
diff --git a/vite.config.js b/vite.config.js
index d83935c..a19a69d 100644
--- a/vite.config.js
+++ b/vite.config.js
@@ -4,7 +4,10 @@ import { ViteEjsPlugin } from 'vite-plugin-ejs';
import { viteSingleFile } from 'vite-plugin-singlefile';
import { TransformEjs } from './src/lib/vite-plugins';
-import { getRenderData } from './src/themes/data';
+import {
+ getRenderData,
+ primaryColorVarName,
+} from './src/themes/data';
const dataFilename = process.env.DATA_FILENAME || './sample.cv.json'
const outDir = process.env.OUT_DIR || 'dist'
@@ -12,6 +15,10 @@ const outDir = process.env.OUT_DIR || 'dist'
const data = require(dataFilename)
const renderData = getRenderData(data)
renderData.theme = process.env.THEME || 'reorx'
+renderData.primaryColor = {
+ var: primaryColorVarName,
+ value: process.env.PRIMARY_COLOR || '#2A3FFB'
+}
renderData.isProduction = process.env.NODE_ENV === 'production'
renderData.meta = {
title: data.basics.name,