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

feat: add themes #167

Draft
wants to merge 2 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion src/lib/components/ui/StickyCard.svelte
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
<aside
class="sticky top-16 left-0 break-words flex flex-shrink
flex-col gap-4 p-6 max-w-full w-full max-h-[calc(100svh-8rem)]
flex-col gap-4 p-6 max-w-full w-full h-full
overflow-auto text-sm border-slate-200 dark:border-zinc-800 {$$props.class}"
>
<slot />
Expand Down
23 changes: 18 additions & 5 deletions src/lib/components/ui/layout/Shell.svelte
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
<script lang="ts">
import { userSettings } from '$lib/settings.js'
import { colors, userSettings } from '$lib/settings.js'
import { routes } from '$lib/util.js'

export let route: { id: string | null } | undefined = undefined
Expand All @@ -11,7 +11,19 @@

<svelte:window bind:scrollY={scroll} />

<div class="shell {$$props.class}">
<div
style:--white={$colors?.['white']}
style:--black={$colors?.['black']}
style:--bg-light-25={$colors?.['bg-light-25']}
style:--bg-light-50={$colors?.['bg-light-50']}
style:--bg-light-100={$colors?.['bg-light-100']}
style:--bg-light-200={$colors?.['bg-light-200']}
style:--bg-dark-925={$colors?.['bg-dark-925']}
style:--bg-dark-950={$colors?.['bg-dark-950']}
style:--primary-900={$colors?.['primary-900']}
style:--primary-100={$colors?.['primary-100']}
class="shell {$$props.class}"
>
<slot />
<slot
name="navbar"
Expand All @@ -29,17 +41,18 @@
>
<slot
name="sidebar"
class="hidden md:flex sticky top-16 left-0 w-full max-w-full"
class="hidden md:flex sticky top-16 left-0 w-full max-w-full bg-slate-50 dark:bg-zinc-950"
style="grid-area: sidebar; width: 100% !important;"
/>
<slot
name="main"
class="w-full bg-slate-25 dark:bg-zinc-925 dark:bg-transparent justify-self-center shadow-sm"
class="w-full bg-slate-25 dark:bg-zinc-925 justify-self-center shadow-sm"
style="grid-area: main"
/>
<slot
name="suffix"
class="max-xl:hidden w-full"
class="max-xl:hidden w-full h-full sticky top-16 left-0 bg-slate-50 dark:bg-zinc-950
max-h-[calc(100svh-4rem)]"
style="grid-area: suffix;"
/>
</div>
Expand Down
41 changes: 39 additions & 2 deletions src/lib/settings.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import type { CommentSortType, SortType } from 'lemmy-js-client'
import { writable } from 'svelte/store'
import { env } from '$env/dynamic/public'
import { browser } from '$app/environment'

console.log('Using the following default settings from the environment:')
console.log(env)
Expand Down Expand Up @@ -122,6 +123,43 @@ export const defaultSettings: Settings = {
separateVotes: false,
}

/**
* Use this for validation.
*/
export const defaultColor: Colors = {
white: '',
black: '',
'bg-light-25': '',
'bg-light-50': '',
'bg-light-100': '',
'bg-light-200': '',
'bg-dark-925': '',
'bg-dark-950': '',
'primary-100': '',
'primary-900': '',
}

interface Colors {
white?: string
black?: string
'bg-light-25'?: string
'bg-light-50'?: string
'bg-light-100'?: string
'bg-light-200'?: string
'bg-dark-925'?: string
'bg-dark-950'?: string
'primary-100'?: string
'primary-900'?: string
}

export const colors = writable<Colors>(
browser ? JSON.parse(localStorage.getItem('colors') ?? '{}') : {}
)
colors.subscribe((c) => {
if (!browser) return
localStorage.setItem('colors', JSON.stringify(c))
})

export const userSettings = writable(defaultSettings)

const migrate = (settings: any): Settings => {
Expand All @@ -138,13 +176,12 @@ const migrate = (settings: any): Settings => {
return settings
}

if (typeof window != 'undefined') {
if (browser) {
let oldUserSettings = JSON.parse(
localStorage.getItem('settings') ?? JSON.stringify(defaultSettings)
)

oldUserSettings = migrate(oldUserSettings)

userSettings.set({ ...defaultSettings, ...oldUserSettings })
}

Expand Down
36 changes: 22 additions & 14 deletions src/routes/settings/+page.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
import Link from '$lib/components/input/Link.svelte'
import {
ArrowPath,
ArrowRight,
ChatBubbleOvalLeftEllipsis,
ChevronDown,
ChevronRight,
Expand All @@ -21,7 +22,6 @@
import { Button, Checkbox, Select } from 'mono-svelte'
import ViewSelect from '$lib/components/lemmy/ViewSelect.svelte'
import { LINKED_INSTANCE_URL } from '$lib/instance.js'
import { removeItem } from '$lib/util.js'

let data = {
loading: false,
Expand All @@ -32,19 +32,22 @@
<title>Settings</title>
</svelte:head>

<h1 class="text-3xl font-bold flex justify-between">
Settings <Button
on:click={() => {
toast({
content: 'Are you sure you want to reset your settings to the default?',
action: () => ($userSettings = defaultSettings),
})
}}
class="font-normal"
>
<Icon src={ArrowPath} mini size="16" slot="prefix" />
Reset to default
</Button>
<h1 class="text-3xl font-bold flex justify-between items-center">
Settings <div class="flex items-center font-normal gap-2">
<Button
on:click={() => {
toast({
content:
'Are you sure you want to reset your settings to the default?',
action: () => ($userSettings = defaultSettings),
})
}}
class="font-normal"
>
<Icon src={ArrowPath} mini size="16" slot="prefix" />
Reset to default
</Button>
</div>
</h1>

<div
Expand All @@ -59,6 +62,11 @@
Instances
</Button>
<Button href="#other" alignment="left" color="tertiary">Other</Button>
<hr class="border-slate-200 dark:border-zinc-800" />
<Button href="/settings/theme" color="tertiary" alignment="left">
<Icon src={ArrowRight} size="16" mini slot="suffix" />
Themes
</Button>
</nav>
<div class="flex flex-col md:flex-1 md:pl-4 min-w-0">
<SectionTitle id="ui" class="mt-4">UI</SectionTitle>
Expand Down
128 changes: 128 additions & 0 deletions src/routes/settings/theme/+page.svelte
Original file line number Diff line number Diff line change
@@ -0,0 +1,128 @@
<script lang="ts">
import { colors, defaultColor } from '$lib/settings'
import { Button, Material, Modal, TextArea, toast } from 'mono-svelte'
import Color from './Color.svelte'

const areKeysEqual = (a: any, b: any) =>
Object.keys(a).every((k) => b.hasOwnProperty(k))

let importTheme = {
text: '',
open: false,
}
</script>

<Modal
title="Import Theme"
action="Import"
on:action={async () => {
try {
const color = importTheme.text
if (!(color || undefined)) throw new Error('There was nothing input.')

const json = JSON.parse(color)
if (!areKeysEqual(json, defaultColor)) {
console.log(json, defaultColor)
throw new Error('Invalid JSON')
}

colors.set(json)
toast({ content: 'Imported theme.' })

importTheme.open = false
} catch (e) {
// @ts-ignore
toast({ content: e, type: 'error' })
}
}}
bind:open={importTheme.open}
>
<TextArea label="JSON" bind:value={importTheme.text} />
</Modal>

<div class="flex flex-col gap-2">
<h1 class="font-bold text-3xl flex flex-row items-center justify-between">
Theming

<Button class="w-max" on:click={() => ($colors = {})}>Reset</Button>
</h1>
<div class="flex flex-row items-center gap-2">
<Button
on:click={() => {
importTheme.text = ''
importTheme.open = true
}}
>
Import
</Button>
<Button
on:click={() => {
navigator?.clipboard?.writeText(JSON.stringify($colors))
toast({ content: 'Copied theme to clipboard.' })
}}
>
Export
</Button>
</div>

<div class="flex flex-col md:flex-row flex-wrap gap-4">
<Material class="flex-1">
<div class="font-bold text-lg mb-2">Light theme</div>
<div class="flex flex-row flex-wrap gap-2 items-center">
<Color fallback="bg-white" bind:value={$colors['white']} name="White" />
<Color
fallback="bg-slate-25"
bind:value={$colors['bg-light-25']}
name="25"
/>
<Color
fallback="bg-slate-50"
bind:value={$colors['bg-light-50']}
name="50"
/>
<Color
fallback="bg-slate-100"
bind:value={$colors['bg-light-100']}
name="100"
/>
<Color
fallback="bg-slate-200"
bind:value={$colors['bg-light-200']}
name="200"
/>
</div>
</Material>
<Material class="flex-1">
<div class="font-bold text-lg mb-2">Dark theme</div>
<div class="flex flex-row flex-wrap gap-2 items-center">
<Color
fallback="bg-zinc-925"
bind:value={$colors['bg-dark-925']}
name="925"
/>
<Color
fallback="bg-zinc-950"
bind:value={$colors['bg-dark-950']}
name="950"
/>
<Color fallback="bg-black" bind:value={$colors['black']} name="Black" />
</div>
</Material>
<Material class="flex-1">
<div class="font-bold text-lg">Primary</div>
<p class="mb-2">Used for buttons</p>
<div class="flex flex-row flex-wrap gap-2 items-center">
<Color
fallback="bg-primary-100"
bind:value={$colors['primary-100']}
name="100"
/>
<Color
fallback="bg-primary-900"
bind:value={$colors['primary-900']}
name="900"
/>
</div>
</Material>
</div>
</div>
24 changes: 24 additions & 0 deletions src/routes/settings/theme/Color.svelte
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
<script lang="ts">
export let name: string
export let value: string = ''
export let fallback: string | undefined = undefined
</script>

<div class="flex flex-col">
<div
class="relative w-12 h-8 cursor-pointer rounded-md border hover:border-slate-400
hover:dark:border-zinc-400 transition-colors dark:border-zinc-800 {fallback}"
style:background-color={value == '' ? '' : value}
style="background-color: {value};"
>
<input
type="color"
class="absolute top-0 left-0 opacity-0 w-full h-full cursor-pointer"
bind:value
/>
</div>
<span class="font-semibold">{name}</span>
{#if value}
<span class="text-xs opacity-70">{value}</span>
{/if}
</div>
3 changes: 2 additions & 1 deletion src/style/app.css
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,8 @@
}

body {
@apply bg-slate-50 text-slate-900 dark:bg-zinc-950 dark:text-zinc-100 accent-slate-950 dark:accent-zinc-50;
@apply bg-slate-50 text-slate-900 dark:bg-zinc-950 dark:text-zinc-100
accent-slate-950 dark:accent-zinc-50;
}

.dark {
Expand Down
15 changes: 10 additions & 5 deletions tailwind.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -22,19 +22,24 @@ export default {
},
},
colors: {
white: 'var(--white,#fff)',
black: 'var(--black,#000)',
slate: {
200: '#e8edf4',
25: 'rgb(252,253,254)',
25: 'var(--bg-light-25,rgb(252,253,254))',
50: 'var(--bg-light-50,#f8fafc)',
100: 'var(--bg-light-100,#f1f5f9)',
200: 'var(--bg-light-200,#e8edf4)',
},
zinc: {
700: '#34343b',
800: '#1f1f24',
900: '#121215',
925: '#0c0c0e',
925: 'var(--bg-dark-925,#0c0c0e)',
950: 'var(--bg-dark-950,#09090b)',
},
primary: {
100: '#f1f5f9',
900: '#0f172a',
100: 'var(--primary-100,#f1f5f9)',
900: 'var(--primary-900,#0f172a)',
},
},
},
Expand Down