diff --git a/manifest.json b/manifest.json index 72db777..a9cbccd 100644 --- a/manifest.json +++ b/manifest.json @@ -1,7 +1,7 @@ { "id": "obsidian-style-settings", "name": "Style Settings", - "version": "0.4.2", + "version": "0.4.3", "minAppVersion": "0.11.5", "description": "Offers controls for adjusting theme, plugin, and snippet CSS variables.", "author": "mgmeyers", diff --git a/package.json b/package.json index 10df0f5..68146f3 100644 --- a/package.json +++ b/package.json @@ -5,7 +5,7 @@ "main": "main.js", "scripts": { "dev": "rollup --config rollup.config.js -w", - "build": "rollup --config rollup.config.js" + "build": "rollup --config rollup.config.js --environment BUILD:production" }, "keywords": [], "author": "", diff --git a/rollup.config.js b/rollup.config.js index 595757b..b82c632 100644 --- a/rollup.config.js +++ b/rollup.config.js @@ -1,10 +1,9 @@ -import dotenv from "dotenv"; import typescript from "@rollup/plugin-typescript"; import { nodeResolve } from "@rollup/plugin-node-resolve"; import commonjs from "@rollup/plugin-commonjs"; import css from "rollup-plugin-css-only"; -dotenv.config(); +const isProd = process.env.BUILD === "production"; const banner = `/* THIS IS A GENERATED/BUNDLED FILE BY ROLLUP @@ -17,6 +16,7 @@ const output = [ input: "./src/main.ts", output: { dir: ".", + sourcemap: isProd ? false : "inline", format: "cjs", exports: "default", banner, @@ -26,7 +26,7 @@ const output = [ css({ output: "styles.css" }), typescript(), nodeResolve({ browser: true }), - commonjs() + commonjs(), ], }, ]; diff --git a/src/SettingsManager.ts b/src/SettingsManager.ts index 30582f4..940a35d 100644 --- a/src/SettingsManager.ts +++ b/src/SettingsManager.ts @@ -64,10 +64,12 @@ function generateColorVariables( case "hsl-values": { const hsl = parsedColor.hsl(); const alpha = opacity ? `,${parsedColor.alpha()}` : ""; + const h = isNaN(hsl[0]) ? 0 : hsl[0]; + return [ { key, - value: `${hsl[0]},${hsl[1] * 100}%,${hsl[2] * 100}%${alpha}`, + value: `${h},${hsl[1] * 100}%,${hsl[2] * 100}%${alpha}`, }, ...alts, ]; diff --git a/src/main.ts b/src/main.ts index 2be025a..07dd426 100644 --- a/src/main.ts +++ b/src/main.ts @@ -1,4 +1,11 @@ -import { App, Plugin, PluginSettingTab, Setting } from "obsidian"; +import { + App, + ItemView, + Plugin, + PluginSettingTab, + Setting, + WorkspaceLeaf, +} from "obsidian"; import { CSSSettingsManager } from "./SettingsManager"; import { CleanupFunction, @@ -21,6 +28,8 @@ type ErrorList = Array<{ name: string; error: string }>; export default class CSSSettingsPlugin extends Plugin { settingsManager: CSSSettingsManager; settingsTab: CSSSettingsTab; + settingsList: ParsedCSSSettings[] = []; + errorList: ErrorList = []; async onload() { this.settingsManager = new CSSSettingsManager(this); @@ -31,6 +40,16 @@ export default class CSSSettingsPlugin extends Plugin { this.addSettingTab(this.settingsTab); + this.registerView(viewType, (leaf) => new SettingsView(this, leaf)); + + this.addCommand({ + id: "show-style-settings-leaf", + name: "Show style settings view", + callback: () => { + this.activateView(); + }, + }); + this.registerEvent( this.app.workspace.on("css-change", () => { this.parseCSS(); @@ -55,8 +74,6 @@ export default class CSSSettingsPlugin extends Plugin { this.debounceTimer = window.setTimeout(() => { const styleSheets = document.styleSheets; - const settingsList: ParsedCSSSettings[] = []; - const errorList: ErrorList = []; for (let i = 0, len = styleSheets.length; i < len; i++) { const sheet = styleSheets.item(i); @@ -86,45 +103,88 @@ export default class CSSSettingsPlugin extends Plugin { } ) as ParsedCSSSettings; - settings.settings = settings.settings.filter(setting => setting); + settings.settings = settings.settings.filter( + (setting) => setting + ); if ( typeof settings === "object" && settings.name && settings.id && - settings.settings && + settings.settings && settings.settings.length ) { - settingsList.push(settings); + this.settingsList.push(settings); } } catch (e) { - errorList.push({ name, error: `${e}` }); + this.errorList.push({ name, error: `${e}` }); } } while ((match = settingRegExp.exec(text)) !== null); } } - this.settingsTab.setSettings(settingsList, errorList); + this.settingsTab.settingsMarkup.setSettings( + this.settingsList, + this.errorList + ); + this.app.workspace.getLeavesOfType(viewType).forEach((leaf) => { + (leaf.view as SettingsView).settingsMarkup.setSettings( + this.settingsList, + this.errorList + ); + }); this.settingsManager.initClasses(); }, 100); } onunload() { - this.settingsManager.cleanup(); - this.settingsTab.cleanup(); document.body.classList.remove("css-settings-manager"); + this.settingsManager.cleanup(); + this.settingsTab.settingsMarkup.cleanup(); + this.deactivateView(); + } + + deactivateView() { + this.app.workspace.detachLeavesOfType(viewType); + } + + async activateView() { + this.deactivateView(); + const leaf = this.app.workspace.createLeafBySplit( + this.app.workspace.activeLeaf, + "vertical" + ); + + await leaf.setViewState({ + type: viewType, + }); + + (leaf.view as SettingsView).settingsMarkup.setSettings( + this.settingsList, + this.errorList + ); } } -class CSSSettingsTab extends PluginSettingTab { +class SettingsMarkup { + app: App; plugin: CSSSettingsPlugin; cleanupFns: CleanupFunction[] = []; settings: ParsedCSSSettings[] = []; errorList: ErrorList = []; - - constructor(app: App, plugin: CSSSettingsPlugin) { - super(app, plugin); + containerEl: HTMLElement; + isView: boolean; + + constructor( + app: App, + plugin: CSSSettingsPlugin, + containerEl: HTMLElement, + isView?: boolean + ) { + this.app = app; this.plugin = plugin; + this.containerEl = containerEl; + this.isView = !!isView; } display(): void { @@ -254,6 +314,7 @@ class CSSSettingsTab extends PluginSettingTab { const cleanup = createSettings({ containerEl, + isView: this.isView, sectionId: s.id, sectionName: s.name, settings: options, @@ -266,3 +327,54 @@ class CSSSettingsTab extends PluginSettingTab { this.cleanupFns = cleanupFns; } } + +class CSSSettingsTab extends PluginSettingTab { + settingsMarkup: SettingsMarkup; + + constructor(app: App, plugin: CSSSettingsPlugin) { + super(app, plugin); + this.settingsMarkup = new SettingsMarkup(app, plugin, this.containerEl); + } + + display(): void { + this.settingsMarkup.display(); + } +} + +const viewType = "style-settings"; + +class SettingsView extends ItemView { + settingsMarkup: SettingsMarkup; + plugin: CSSSettingsPlugin; + + constructor(plugin: CSSSettingsPlugin, leaf: WorkspaceLeaf) { + super(leaf); + this.plugin = plugin; + this.settingsMarkup = new SettingsMarkup( + plugin.app, + plugin, + this.contentEl, + true + ); + } + + getViewType() { + return viewType; + } + + getIcon() { + return "gear"; + } + + getDisplayText() { + return "Style Settings"; + } + + async onOpen() { + return this.settingsMarkup.display(); + } + + async onClose() { + return this.settingsMarkup.cleanup(); + } +} diff --git a/src/settingHandlers.ts b/src/settingHandlers.ts index f4774e9..c6cfa52 100644 --- a/src/settingHandlers.ts +++ b/src/settingHandlers.ts @@ -35,7 +35,7 @@ function createDescription( if (def) { const small = createEl("small"); - small.appendChild(createEl("strong", { text: `${t('Default:')} ` })); + small.appendChild(createEl("strong", { text: `${t("Default:")} ` })); small.appendChild(document.createTextNode(defLabel || def)); const div = createEl("div"); @@ -125,7 +125,10 @@ function getTitle(config: T): string { function getDescription(config: T): string { if (lang) { - return config[`description.${lang}` as keyof WithDescription] || config.description; + return ( + config[`description.${lang}` as keyof WithDescription] || + config.description + ); } return config.description; @@ -223,13 +226,13 @@ export function createClassToggle(opts: { } }); - toggleComponent = toggle; + toggleComponent = toggle; }) .addExtraButton((b) => { b.setIcon("reset"); b.onClick(() => { const value = !!config.default; - + toggleComponent.setValue(value); if (value) { @@ -269,7 +272,9 @@ export function createClassMultiToggle(opts: { let dropdownComponent: DropdownComponent; if (typeof config.default !== "string") { - return console.error(`${t('Error:')} ${getTitle(config)} ${t('missing default value')}`); + return console.error( + `${t("Error:")} ${getTitle(config)} ${t("missing default value")}` + ); } let prevValue = settingsManager.getSetting(sectionId, config.id) as @@ -372,7 +377,9 @@ export function createVariableText(opts: { let textComponent: TextComponent; if (typeof config.default !== "string") { - return console.error(`${t('Error:')} ${getTitle(config)} ${t('missing default value')}`); + return console.error( + `${t("Error:")} ${getTitle(config)} ${t("missing default value")}` + ); } new Setting(containerEl) @@ -422,12 +429,16 @@ export function createVariableNumber(opts: { let textComponent: TextComponent; if (typeof config.default !== "number") { - return console.error(`${t('Error:')} ${getTitle(config)} ${t('missing default value')}`); + return console.error( + `${t("Error:")} ${getTitle(config)} ${t("missing default value")}` + ); } new Setting(containerEl) .setName(getTitle(config)) - .setDesc(createDescription(getDescription(config), config.default.toString(10))) + .setDesc( + createDescription(getDescription(config), config.default.toString(10)) + ) .addText((text) => { const value = settingsManager.getSetting(sectionId, config.id); const onChange = debounce( @@ -482,12 +493,16 @@ export function createVariableNumberSlider(opts: { let sliderComponent: SliderComponent; if (typeof config.default !== "number") { - return console.error(`${t('Error:')} ${getTitle(config)} ${t('missing default value')}`); + return console.error( + `${t("Error:")} ${getTitle(config)} ${t("missing default value")}` + ); } new Setting(containerEl) .setName(getTitle(config)) - .setDesc(createDescription(getDescription(config), config.default.toString(10))) + .setDesc( + createDescription(getDescription(config), config.default.toString(10)) + ) .addSlider((slider) => { const value = settingsManager.getSetting(sectionId, config.id); const onChange = debounce( @@ -534,7 +549,9 @@ export function createVariableSelect(opts: { let dropdownComponent: DropdownComponent; if (typeof config.default !== "string") { - return console.error(`${t('Error:')} ${getTitle(config)} ${t('missing default value')}`); + return console.error( + `${t("Error:")} ${getTitle(config)} ${t("missing default value")}` + ); } const defaultOption = config.default @@ -609,17 +626,18 @@ export interface VariableColor extends Meta { } function getPickrSettings(opts: { + isView: boolean; el: HTMLElement; containerEl: HTMLElement; swatches: string[]; opacity: boolean | undefined; defaultColor: string; }): Pickr.Options { - const { el, containerEl, swatches, opacity, defaultColor } = opts; + const { el, isView, containerEl, swatches, opacity, defaultColor } = opts; return { el, - container: containerEl, + container: isView ? document.body : containerEl, theme: "nano", swatches, lockOpacity: !opacity, @@ -653,15 +671,18 @@ export function createVariableColor(opts: { config: VariableColor; containerEl: HTMLElement; settingsManager: CSSSettingsManager; + isView: boolean; }): CleanupFunction { - const { sectionId, config, containerEl, settingsManager } = opts; + const { isView, sectionId, config, containerEl, settingsManager } = opts; if ( typeof config.default !== "string" || !isValidDefaultColor(config.default) ) { return console.error( - `${t('Error:')} ${getTitle(config)} ${t('missing default value, or value is not in a valid color format')}` + `${t("Error:")} ${getTitle(config)} ${t( + "missing default value, or value is not in a valid color format" + )}` ); } @@ -686,6 +707,7 @@ export function createVariableColor(opts: { pickr = Pickr.create( getPickrSettings({ + isView, el: setting.controlEl.createDiv({ cls: "picker" }), containerEl, swatches, @@ -742,15 +764,18 @@ export function createVariableThemedColor(opts: { config: VariableThemedColor; containerEl: HTMLElement; settingsManager: CSSSettingsManager; + isView: boolean; }): CleanupFunction { - const { sectionId, config, containerEl, settingsManager } = opts; + const { sectionId, isView, config, containerEl, settingsManager } = opts; if ( typeof config["default-light"] !== "string" || !isValidDefaultColor(config["default-light"]) ) { return console.error( - `${t('Error:')} ${getTitle(config)} ${t('missing default light value, or value is not in a valid color format')}` + `${t("Error:")} ${getTitle(config)} ${t( + "missing default light value, or value is not in a valid color format" + )}` ); } @@ -759,7 +784,9 @@ export function createVariableThemedColor(opts: { !isValidDefaultColor(config["default-dark"]) ) { return console.error( - `${t('Error:')} ${getTitle(config)} ${t('missing default dark value, or value is not in a valid color format')}` + `${t("Error:")} ${getTitle(config)} ${t( + "missing default dark value, or value is not in a valid color format" + )}` ); } @@ -831,6 +858,7 @@ export function createVariableThemedColor(opts: { wrapper.createDiv({ cls: "theme-light" }, (themeWrapper) => { pickrLight = Pickr.create( getPickrSettings({ + isView, el: themeWrapper.createDiv({ cls: "picker" }), containerEl, swatches: swatchesLight, @@ -864,6 +892,7 @@ export function createVariableThemedColor(opts: { wrapper.createDiv({ cls: "theme-dark" }, (themeWrapper) => { pickrDark = Pickr.create( getPickrSettings({ + isView, el: themeWrapper.createDiv({ cls: "picker" }), containerEl, swatches: swatchesDark, @@ -920,13 +949,20 @@ export interface ParsedCSSSettings { export function createSettings(opts: { containerEl: HTMLElement; + isView: boolean; sectionId: string; sectionName: string; settings: CSSSetting[]; settingsManager: CSSSettingsManager; }): CleanupFunction[] { - const { containerEl, sectionId, settings, settingsManager, sectionName } = - opts; + const { + isView, + containerEl, + sectionId, + settings, + settingsManager, + sectionName, + } = opts; const containerStack: HTMLElement[] = [containerEl]; const idStack: string[] = [sectionId]; @@ -1089,6 +1125,7 @@ export function createSettings(opts: { config: setting as VariableColor, containerEl: getTargetContainer(containerStack), settingsManager, + isView, }) ); break; @@ -1102,6 +1139,7 @@ export function createSettings(opts: { config: setting as VariableThemedColor, containerEl: getTargetContainer(containerStack), settingsManager, + isView, }) ); break; diff --git a/src/settings.css b/src/settings.css index eb23003..d473e40 100644 --- a/src/settings.css +++ b/src/settings.css @@ -164,3 +164,16 @@ .style-settings-import-error.active { display: block; } + +.view-content .style-settings-container .setting-item:not(.setting-item-heading) { + flex-direction: column; + align-items: flex-start; +} + +.view-content .style-settings-container .setting-item:not(.setting-item-heading) .setting-item-control { + padding-top: 0.5em; +} + +.view-content .style-settings-container .setting-item:not(.setting-item-heading) .themed-color-wrapper { + display: flex; +} \ No newline at end of file diff --git a/styles.css b/styles.css index f3c49d5..16a0b3c 100644 --- a/styles.css +++ b/styles.css @@ -1,36 +1,3 @@ -.pcr-app .pcr-swatches > button { - padding: 0; -} - -.pickr .pcr-button { - margin-right: 0; -} - -.themed-color-wrapper > div { - background: var(--background-primary); - padding: 10px; - display: flex; - align-items: center; - border-radius: 4px; -} - -.themed-color-wrapper > div + div { - margin-top: 6px; -} - -.themed-color-wrapper button { - display: block; -} - -.themed-color-wrapper .pickr-reset > button { - margin: 0 0 0 10px; - padding: 9px; - line-height: 1; -} - -.themed-color-wrapper .pickr-reset > button > svg { - display: block; -} .style-settings-heading { cursor: pointer; margin-bottom: 18px; @@ -197,6 +164,51 @@ .style-settings-import-error.active { display: block; } + +.view-content .style-settings-container .setting-item:not(.setting-item-heading) { + flex-direction: column; + align-items: flex-start; +} + +.view-content .style-settings-container .setting-item:not(.setting-item-heading) .setting-item-control { + padding-top: 0.5em; +} + +.view-content .style-settings-container .setting-item:not(.setting-item-heading) .themed-color-wrapper { + display: flex; +}.pcr-app .pcr-swatches > button { + padding: 0; +} + +.pickr .pcr-button { + margin-right: 0; +} + +.themed-color-wrapper > div { + background: var(--background-primary); + padding: 10px; + display: flex; + align-items: center; + border-radius: 4px; +} + +.themed-color-wrapper > div + div { + margin-top: 6px; +} + +.themed-color-wrapper button { + display: block; +} + +.themed-color-wrapper .pickr-reset > button { + margin: 0 0 0 10px; + padding: 9px; + line-height: 1; +} + +.themed-color-wrapper .pickr-reset > button > svg { + display: block; +} /*! Pickr 1.8.2 MIT | https://github.com/Simonwep/pickr */ .pickr{position:relative;overflow:visible;transform:translateY(0)}.pickr *{box-sizing:border-box;outline:none;border:none;-webkit-appearance:none}.pickr .pcr-button{position:relative;height:2em;width:2em;padding:0.5em;cursor:pointer;font-family:-apple-system,BlinkMacSystemFont,"Segoe UI","Roboto","Helvetica Neue",Arial,sans-serif;border-radius:.15em;background:url('data:image/svg+xml;utf8, ') no-repeat center;background-size:0;transition:all 0.3s}.pickr .pcr-button::before{position:absolute;content:'';top:0;left:0;width:100%;height:100%;background:url('data:image/svg+xml;utf8, ');background-size:.5em;border-radius:.15em;z-index:-1}.pickr .pcr-button::before{z-index:initial}.pickr .pcr-button::after{position:absolute;content:'';top:0;left:0;height:100%;width:100%;transition:background 0.3s;background:var(--pcr-color);border-radius:.15em}.pickr .pcr-button.clear{background-size:70%}.pickr .pcr-button.clear::before{opacity:0}.pickr .pcr-button.clear:focus{box-shadow:0 0 0 1px rgba(255,255,255,0.85),0 0 0 3px var(--pcr-color)}.pickr .pcr-button.disabled{cursor:not-allowed}.pickr *,.pcr-app *{box-sizing:border-box;outline:none;border:none;-webkit-appearance:none}.pickr input:focus,.pickr input.pcr-active,.pickr button:focus,.pickr button.pcr-active,.pcr-app input:focus,.pcr-app input.pcr-active,.pcr-app button:focus,.pcr-app button.pcr-active{box-shadow:0 0 0 1px rgba(255,255,255,0.85),0 0 0 3px var(--pcr-color)}.pickr .pcr-palette,.pickr .pcr-slider,.pcr-app .pcr-palette,.pcr-app .pcr-slider{transition:box-shadow 0.3s}.pickr .pcr-palette:focus,.pickr .pcr-slider:focus,.pcr-app .pcr-palette:focus,.pcr-app .pcr-slider:focus{box-shadow:0 0 0 1px rgba(255,255,255,0.85),0 0 0 3px rgba(0,0,0,0.25)}.pcr-app{position:fixed;display:flex;flex-direction:column;z-index:10000;border-radius:0.1em;background:#fff;opacity:0;visibility:hidden;transition:opacity 0.3s, visibility 0s 0.3s;font-family:-apple-system,BlinkMacSystemFont,"Segoe UI","Roboto","Helvetica Neue",Arial,sans-serif;box-shadow:0 0.15em 1.5em 0 rgba(0,0,0,0.1),0 0 1em 0 rgba(0,0,0,0.03);left:0;top:0}.pcr-app.visible{transition:opacity 0.3s;visibility:visible;opacity:1}.pcr-app .pcr-swatches{display:flex;flex-wrap:wrap;margin-top:0.75em}.pcr-app .pcr-swatches.pcr-last{margin:0}@supports (display: grid){.pcr-app .pcr-swatches{display:grid;align-items:center;grid-template-columns:repeat(auto-fit, 1.75em)}}.pcr-app .pcr-swatches>button{font-size:1em;position:relative;width:calc(1.75em - 5px);height:calc(1.75em - 5px);border-radius:0.15em;cursor:pointer;margin:2.5px;flex-shrink:0;justify-self:center;transition:all 0.15s;overflow:hidden;background:transparent;z-index:1}.pcr-app .pcr-swatches>button::before{position:absolute;content:'';top:0;left:0;width:100%;height:100%;background:url('data:image/svg+xml;utf8, ');background-size:6px;border-radius:.15em;z-index:-1}.pcr-app .pcr-swatches>button::after{content:'';position:absolute;top:0;left:0;width:100%;height:100%;background:var(--pcr-color);border:1px solid rgba(0,0,0,0.05);border-radius:0.15em;box-sizing:border-box}.pcr-app .pcr-swatches>button:hover{filter:brightness(1.05)}.pcr-app .pcr-swatches>button:not(.pcr-active){box-shadow:none}.pcr-app .pcr-interaction{display:flex;flex-wrap:wrap;align-items:center;margin:0 -0.2em 0 -0.2em}.pcr-app .pcr-interaction>*{margin:0 0.2em}.pcr-app .pcr-interaction input{letter-spacing:0.07em;font-size:0.75em;text-align:center;cursor:pointer;color:#75797e;background:#f1f3f4;border-radius:.15em;transition:all 0.15s;padding:0.45em 0.5em;margin-top:0.75em}.pcr-app .pcr-interaction input:hover{filter:brightness(0.975)}.pcr-app .pcr-interaction input:focus{box-shadow:0 0 0 1px rgba(255,255,255,0.85),0 0 0 3px rgba(66,133,244,0.75)}.pcr-app .pcr-interaction .pcr-result{color:#75797e;text-align:left;flex:1 1 8em;min-width:8em;transition:all 0.2s;border-radius:.15em;background:#f1f3f4;cursor:text}.pcr-app .pcr-interaction .pcr-result::-moz-selection{background:#4285f4;color:#fff}.pcr-app .pcr-interaction .pcr-result::selection{background:#4285f4;color:#fff}.pcr-app .pcr-interaction .pcr-type.active{color:#fff;background:#4285f4}.pcr-app .pcr-interaction .pcr-save,.pcr-app .pcr-interaction .pcr-cancel,.pcr-app .pcr-interaction .pcr-clear{color:#fff;width:auto}.pcr-app .pcr-interaction .pcr-save,.pcr-app .pcr-interaction .pcr-cancel,.pcr-app .pcr-interaction .pcr-clear{color:#fff}.pcr-app .pcr-interaction .pcr-save:hover,.pcr-app .pcr-interaction .pcr-cancel:hover,.pcr-app .pcr-interaction .pcr-clear:hover{filter:brightness(0.925)}.pcr-app .pcr-interaction .pcr-save{background:#4285f4}.pcr-app .pcr-interaction .pcr-clear,.pcr-app .pcr-interaction .pcr-cancel{background:#f44250}.pcr-app .pcr-interaction .pcr-clear:focus,.pcr-app .pcr-interaction .pcr-cancel:focus{box-shadow:0 0 0 1px rgba(255,255,255,0.85),0 0 0 3px rgba(244,66,80,0.75)}.pcr-app .pcr-selection .pcr-picker{position:absolute;height:18px;width:18px;border:2px solid #fff;border-radius:100%;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}.pcr-app .pcr-selection .pcr-color-palette,.pcr-app .pcr-selection .pcr-color-chooser,.pcr-app .pcr-selection .pcr-color-opacity{position:relative;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;display:flex;flex-direction:column;cursor:grab;cursor:-webkit-grab}.pcr-app .pcr-selection .pcr-color-palette:active,.pcr-app .pcr-selection .pcr-color-chooser:active,.pcr-app .pcr-selection .pcr-color-opacity:active{cursor:grabbing;cursor:-webkit-grabbing}.pcr-app[data-theme='nano']{width:14.25em;max-width:95vw}.pcr-app[data-theme='nano'] .pcr-swatches{margin-top:.6em;padding:0 .6em}.pcr-app[data-theme='nano'] .pcr-interaction{padding:0 .6em .6em .6em}.pcr-app[data-theme='nano'] .pcr-selection{display:grid;grid-gap:.6em;grid-template-columns:1fr 4fr;grid-template-rows:5fr auto auto;align-items:center;height:10.5em;width:100%;align-self:flex-start}.pcr-app[data-theme='nano'] .pcr-selection .pcr-color-preview{grid-area:2 / 1 / 4 / 1;height:100%;width:100%;display:flex;flex-direction:row;justify-content:center;margin-left:.6em}.pcr-app[data-theme='nano'] .pcr-selection .pcr-color-preview .pcr-last-color{display:none}.pcr-app[data-theme='nano'] .pcr-selection .pcr-color-preview .pcr-current-color{position:relative;background:var(--pcr-color);width:2em;height:2em;border-radius:50em;overflow:hidden}.pcr-app[data-theme='nano'] .pcr-selection .pcr-color-preview .pcr-current-color::before{position:absolute;content:'';top:0;left:0;width:100%;height:100%;background:url('data:image/svg+xml;utf8, ');background-size:.5em;border-radius:.15em;z-index:-1}.pcr-app[data-theme='nano'] .pcr-selection .pcr-color-palette{grid-area:1 / 1 / 2 / 3;width:100%;height:100%;z-index:1}.pcr-app[data-theme='nano'] .pcr-selection .pcr-color-palette .pcr-palette{border-radius:.15em;width:100%;height:100%}.pcr-app[data-theme='nano'] .pcr-selection .pcr-color-palette .pcr-palette::before{position:absolute;content:'';top:0;left:0;width:100%;height:100%;background:url('data:image/svg+xml;utf8, ');background-size:.5em;border-radius:.15em;z-index:-1}.pcr-app[data-theme='nano'] .pcr-selection .pcr-color-chooser{grid-area:2 / 2 / 2 / 2}.pcr-app[data-theme='nano'] .pcr-selection .pcr-color-opacity{grid-area:3 / 2 / 3 / 2}.pcr-app[data-theme='nano'] .pcr-selection .pcr-color-chooser,.pcr-app[data-theme='nano'] .pcr-selection .pcr-color-opacity{height:0.5em;margin:0 .6em}.pcr-app[data-theme='nano'] .pcr-selection .pcr-color-chooser .pcr-picker,.pcr-app[data-theme='nano'] .pcr-selection .pcr-color-opacity .pcr-picker{top:50%;transform:translateY(-50%)}.pcr-app[data-theme='nano'] .pcr-selection .pcr-color-chooser .pcr-slider,.pcr-app[data-theme='nano'] .pcr-selection .pcr-color-opacity .pcr-slider{flex-grow:1;border-radius:50em}.pcr-app[data-theme='nano'] .pcr-selection .pcr-color-chooser .pcr-slider{background:linear-gradient(to right, red, #ff0, lime, cyan, blue, #f0f, red)}.pcr-app[data-theme='nano'] .pcr-selection .pcr-color-opacity .pcr-slider{background:linear-gradient(to right, transparent, black),url('data:image/svg+xml;utf8, ');background-size:100%, 0.25em}