Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Theme Generator Overhaul #278

Merged
merged 11 commits into from
Sep 26, 2022
64 changes: 35 additions & 29 deletions src/app.html
Original file line number Diff line number Diff line change
@@ -1,35 +1,41 @@
<!DOCTYPE html>
<html lang="en" class="dark">
<head>
<title>Skeleton — A Component Library for Svelte + Tailwind</title>

<!-- Meta Tags -->
<meta charset="utf-8" />
<meta name="description" content="Skeleton is a fully featured Svelte UI component library that allows you to build fast and reactive web UI using Svelte + Tailwind." />
<meta name="keywords" content="svelte, components, svelte components, ui components, sveltekit, vite, astro, framework" />
<meta name="theme-color" content="#22c55e" />
<head>
<title>Skeleton — A Component Library for Svelte + Tailwind</title>

<!-- Open Graph - https://ogp.me/ -->
<meta content="Skeleton" property="og:site_name" />
<meta content="website" property="og:type" />
<meta name="og:description" content="Skeleton is a fully featured Svelte UI component library that allows you to build fast and reactive web UI using Svelte + Tailwind." />
<!-- Meta Tags -->
<meta charset="utf-8" />
<meta name="description"
content="Skeleton is a fully featured Svelte UI component library that allows you to build fast and reactive web UI using Svelte + Tailwind." />
<meta name="keywords"
content="svelte, components, svelte components, ui components, sveltekit, vite, astro, framework" />
<meta name="theme-color" content="#22c55e" />

<link rel="icon" href="%sveltekit.assets%/favicon.png" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
%sveltekit.head%
</head>
<body>
%sveltekit.body%
</body>
<!-- Open Graph - https://ogp.me/ -->
<meta content="Skeleton" property="og:site_name" />
<meta content="website" property="og:type" />
<meta name="og:description"
content="Skeleton is a fully featured Svelte UI component library that allows you to build fast and reactive web UI using Svelte + Tailwind." />

<!-- Google Analytics -->
<script async src="https://www.googletagmanager.com/gtag/js?id=G-FVKTKV7HDL"></script>
<script>
window.dataLayer = window.dataLayer || [];
function gtag() {
dataLayer.push(arguments);
}
gtag('js', new Date());
gtag('config', 'G-FVKTKV7HDL');
</script>
</html>
<link rel="icon" href="%sveltekit.assets%/favicon.png" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
%sveltekit.head%
</head>

<body class="bg-mesh">
%sveltekit.body%
</body>

<!-- Google Analytics -->
<script async src="https://www.googletagmanager.com/gtag/js?id=G-FVKTKV7HDL"></script>
<script>
window.dataLayer = window.dataLayer || [];
function gtag() {
dataLayer.push(arguments);
}
gtag('js', new Date());
gtag('config', 'G-FVKTKV7HDL');
</script>

</html>
9 changes: 6 additions & 3 deletions src/app.postcss
Original file line number Diff line number Diff line change
Expand Up @@ -8,16 +8,19 @@ body {
@apply h-full overflow-hidden;
}

/* Gradient Background, via: https://csshero.org/mesher/ */
body {
/*
Background Mesh Gradient - this is applied to the <body> element in app.html
https://csshero.org/mesher/
*/
.bg-mesh {
/* prettier-ignore */
background-image:
radial-gradient(at 35% 35%, hsla(160,84%,39%,0.12) 0px, transparent 35%),
radial-gradient(at 61% 60%, hsla(238,83%,66%,0.16) 0px, transparent 35%);
background-size: cover;
background-attachment: fixed;
}
.dark body {
.dark .bg-mesh {
/* prettier-ignore */
background-image:
radial-gradient(at 35% 35%, hsla(160,84%,39%,0.18) 0px, transparent 50%),
Expand Down
6 changes: 3 additions & 3 deletions src/docs/DocsAppBar/DocsAppBar.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -30,9 +30,9 @@
<!-- Branding -->
<svelte:fragment slot="lead">
<!-- Drawer Menu -->
<div on:click={drawerOpen} class="lg:hidden mr-2 p-1 cursor-pointer">
<SvgIcon name="bars" width="w-6" height="h-6" fill="fill-black dark:fill-white" on:click={drawerOpen} />
</div>
<button on:click={drawerOpen} class="lg:hidden mr-2 p-1 cursor-pointer">
<SvgIcon name="bars" width="w-6" height="h-6" fill="fill-black dark:fill-white" />
</button>
<!-- Skeleton -->
<a href="/" class="text-sm sm:text-lg md:text-3xl font-bold uppercase mr-4" title="Return to Homepage">Skeleton</a>
<!-- Badge -->
Expand Down
9 changes: 2 additions & 7 deletions src/docs/DocsNavigation/links.ts
Original file line number Diff line number Diff line change
Expand Up @@ -64,12 +64,6 @@ export const menuNavLinks: any = [
// Work in Progress
// {href: '/components/conic-gradients', label: 'Conic Gradients'}, // keep disabled until further notice
// { href: '/components/svg-icons', label: 'SVG Icons' }, // keep disabled until further notice
// Depricated
// { href: '/components/badges', label: 'Badges', badge: 'Depricated' },
// { href: '/components/buttons', label: 'Buttons', badge: 'Depricated' },
// { href: '/components/cards', label: 'Cards', badge: 'Depricated' },
// { href: '/components/logo-clouds', label: 'Logo Clouds', badge: 'Depricated' },
// { href: '/components/placeholders', label: 'Placeholders', badge: 'Depricated' }
]
},
{
Expand All @@ -88,7 +82,8 @@ export const menuNavLinks: any = [
{ href: '/utilities/drawers', label: 'Drawers' },
{ href: '/utilities/dialogs', label: 'Dialogs' },
{ href: '/utilities/toasts', label: 'Toasts' },
{ href: '/utilities/lightswitches', label: 'Lightswitch' }
{ href: '/utilities/lightswitches', label: 'Lightswitch' },
{ href: '/utilities/local-storage-stores', label: 'Local Storage Stores' }
]
}
];
180 changes: 180 additions & 0 deletions src/docs/DocsThemer/DocsThemer.svelte
Original file line number Diff line number Diff line change
@@ -0,0 +1,180 @@
<script lang="ts">
import { browser } from '$app/environment';
import type { Writable } from 'svelte/store';

// Utilities
import { localStorageStore } from '$lib/utilities/LocalStorageStore/LocalStorageStore';

// Components
import CodeBlock from '$lib/utilities/CodeBlock/CodeBlock.svelte';
import RadioGroup from '$lib/components/Radio/RadioGroup.svelte';
import RadioItem from '$lib/components/Radio/RadioItem.svelte';
import SlideToggle from '$lib/components/SlideToggle/SlideToggle.svelte';
import Swatches from './Swatches.svelte';

// Helpers
import { getTailwindColor, randomTailwindColor, genHexPalette, generateThemeCss } from './helpers';
import { colorsTailwind } from './colors';

// Local
const regexValidHexCode = new RegExp(/^#[0-9a-f]{6}$/i);
let themeCss: string = '';

// Stores
let storeMode: Writable<boolean> = localStorageStore('storeMode', true); // T: Tailwind | F: Custom
let storePreview: Writable<boolean> = localStorageStore('storePreview', false);
let storeTailwindForm: Writable<any> = localStorageStore('storeTailwindForm', {
primary: 'emerald',
accent: 'indigo',
ternary: 'yellow',
warning: 'rose',
surface: 'gray'
});
let storeTailwindPalette: Writable<any> = localStorageStore('storeTailwindPalette', {
primary: getTailwindColor($storeTailwindForm.primary),
accent: getTailwindColor($storeTailwindForm.accent),
ternary: getTailwindColor($storeTailwindForm.ternary),
warning: getTailwindColor($storeTailwindForm.warning),
surface: getTailwindColor($storeTailwindForm.surface)
});
let storeHexForm: Writable<any> = localStorageStore('storeHexForm', {
primary: getTailwindColor('emerald').shades['500'].hex,
accent: getTailwindColor('indigo').shades['500'].hex,
ternary: getTailwindColor('yellow').shades['500'].hex,
warning: getTailwindColor('rose').shades['500'].hex,
surface: getTailwindColor('gray').shades['500'].hex
});
let storeHexPalette: Writable<any> = localStorageStore('storeHexPalette', {
primary: genHexPalette('primary', $storeHexForm.primary),
accent: genHexPalette('accent', $storeHexForm.accent),
ternary: genHexPalette('ternary', $storeHexForm.ternary),
warning: genHexPalette('warning', $storeHexForm.warning),
surface: genHexPalette('surface', $storeHexForm.surface)
});

// Local Storage

function resetSettings(): void {
if (confirm('Clear all theme settings and restore the site back to the default settings?')) {
// Clear Local Storage Values
localStorage.removeItem('storeMode');
localStorage.removeItem('storePreview');
localStorage.removeItem('storeTailwindForm');
localStorage.removeItem('storeTailwindPalette');
localStorage.removeItem('storeHexForm');
localStorage.removeItem('storeHexPalette');
// Reload Window
location.reload();
}
}

// Functions ---

// Tailwind: on selection change
function onTailwindSelect(): void {
storeTailwindPalette.set({
primary: getTailwindColor($storeTailwindForm.primary),
accent: getTailwindColor($storeTailwindForm.accent),
ternary: getTailwindColor($storeTailwindForm.ternary),
warning: getTailwindColor($storeTailwindForm.warning),
surface: getTailwindColor($storeTailwindForm.surface)
});
// Generate CSS
setThemeCss();
}

// Tailwind: on randomize button click
function onRandomize(): void {
storeTailwindPalette.set({
primary: randomTailwindColor(),
accent: randomTailwindColor(),
warning: randomTailwindColor(),
ternary: randomTailwindColor(),
surface: randomTailwindColor()
});
}

// Hex: on input change
function onHexInput(key: string, hexColor: string): void {
if (regexValidHexCode.test(hexColor)) {
// Generate Palette
$storeHexPalette[key] = genHexPalette(key, hexColor);
// Update CSS Snipet
setThemeCss();
}
}

// Shared: Generate CSS Snippet
function setThemeCss(): void {
themeCss = generateThemeCss($storeMode, currentPalette);
}

// Reactive ---

// Set the current palette based on Tailwind/Hex mode
$: currentPalette = $storeMode === true ? $storeTailwindPalette : $storeHexPalette;

// Update the CSS snippet on current palette change
$: if (currentPalette) setThemeCss();

// Toggle `.bg-mesh` on body when preview mobile ON
$: if (browser) document.body.classList.toggle('bg-mesh', !$storePreview);
</script>

<!-- Insert live theme into page head -->
<svelte:head>
{@html $storePreview ? `\<style\>${themeCss}\</style\>` : ''}
</svelte:head>

<!-- prettier-ignore -->
<div class="themer space-y-4">



<!-- Color Pickers -->
<section class="card !bg-[#141517] !ring-0">

<!-- Header: Controls -->
<div class="card-header flex justify-between items-center space-x-4">
<!-- Mode -->
<RadioGroup selected={storeMode}>
<RadioItem value={true}>Tailwind</RadioItem>
<RadioItem value={false}>Hex</RadioItem>
</RadioGroup>
{#if $storeMode}<button class="btn btn-filled" on:click={onRandomize}>Randomize</button>{/if}
{#if !$storeMode}<a class="btn btn-filled" href="https://coolors.co/" target="_blank">Inspiration</a>{/if}
</div>

<!-- Body: Form -->
<div class="card-body !pt-2 space-y-4 lg:space-y-2">
{#each ['primary', 'accent', 'ternary', 'warning', 'surface'] as colorKey}
<div class="grid grid-cols-1 xl:grid-cols-[140px_1fr] gap-2 xl:gap-4 xl:place-items-end">
<label class="w-full">
<span class="text-white capitalize">{colorKey}</span>
{#if $storeMode}
<select class="capitalize" bind:value={$storeTailwindForm[colorKey]} on:change={()=>{onTailwindSelect()}}>
{#each colorsTailwind as c}<option value={c.label}>{c.label}</option>{/each}
</select>
{:else}
<input
type="text" placeholder="#FFFFFF" bind:value={$storeHexForm[colorKey]}
on:input={() => { onHexInput(colorKey, $storeHexForm[colorKey]); }}
/>
{/if}
</label>
<Swatches palette={currentPalette[colorKey].shades} />
</div>
{/each}
</div>

<!-- Live Preview -->
<div class="card-footer !pt-4 {$storePreview ? 'bg-green-500/30' : 'bg-red-500/10'} flex justify-between items-center">
<SlideToggle bind:checked={$storePreview} class="text-white">Enable Live Preview</SlideToggle>
<button class="btn bg-white/5 text-white" on:click={resetSettings}>Reset Theme</button>
</div>

</section>

<!-- CSS Snipnpet -->
<CodeBlock language="css" code={themeCss} />
</div>
Loading