your personal website on atproto - mirror blento.app

add theme colors selection

Florian 45881e8c 77a880cb

+226 -18
+9 -7
docs/Beta.md
··· 3 3 - site.standard 4 4 - move description to markdownDescription and set description as text only 5 5 6 + - allow editing on mobile 7 + 8 + - get automatic layout for mobile if only edited on desktop (and vice versa) 9 + 10 + - add cards in middle of current position (both mobile and desktop version) 11 + 12 + - show nsfw warnings 13 + 6 14 - big button card 7 15 8 16 - card with big call to action button 9 17 10 - - link card allow changing favicon, og image (+ hide favicon) 11 - 12 18 - video card? 13 19 14 20 - allow setting base and accent color 15 21 16 22 - ask to fill with some default cards on page creation 17 23 18 - - share button (copy share link to blento, maybe post to bluesky?) 19 - 20 - - add icons to "change card to..." popover 21 - 22 24 - when adding images try to add them in a size that best fits aspect ratio 23 25 24 26 - onboarding 25 27 26 - - show alert when user tries to close window with unsaved changes 28 + - fix invalid handle thing
+12 -7
docs/CardIdeas.md
··· 3 3 ## media 4 4 5 5 - general video card 6 - - inline youtube video 6 + - [x] inline youtube video 7 7 - cartoons: aka https://www.opendoodles.com/ 8 8 - excalidraw (/svg card) 9 9 - latest blog post (e.g. leaflet) 10 10 - fake 3d image (with depth map) 11 - - fluid text effect (https://flo-bit.dev/projects/fluid-text-effect/) 12 - - gifs 13 - - little drawing app 11 + - [x] fluid text effect (https://flo-bit.dev/projects/fluid-text-effect/) 12 + - [x] gifs 13 + - [x] little drawing app 14 14 - css voxel art 15 15 - 3d model 16 + - spotify or apple music playlist 16 17 17 18 ## social accounts 18 19 19 20 - instagram card (showing follow button, follower count, latest posts) 20 - - github card (showing activity grid) 21 + - [x] github card (showing activity grid) 21 22 - bluesky account card (showing follow button, follower count, avatar, name, cover image) 22 23 - youtube channel card (showing channel name, latest videos, follow button?) 23 24 - bluesky posts workcloud ··· 40 41 - teal.fm 41 42 - [x] last played songs 42 43 - tangled.sh 44 + - pinned repos 45 + - activity heatmap? 43 46 - popfeed.social 44 47 - reading goal 45 48 - [x] latest ratings 46 49 - lists 47 - - smokesignal.events (https://pdsls.dev/at://did:plc:xbtmt2zjwlrfegqvch7fboei/events.smokesignal.calendar.event/3ltn2qrxf3626) 48 - - statusphere.xyz (https://googlefonts.github.io/noto-emoji-animation/, https://gist.github.com/sanjacob/a0ccdf6d88f15bf158d8895090722d14) 50 + - smokesignal.events 51 + - [x] specific event 52 + - all future events i'm hosting/attending 53 + - [x] statusphere.xyz (TODO: assing to specific record) 49 54 - goals.garden 50 55 - flashes.blue (https://pdsls.dev/at://did:plc:aytgljyikzbtgrnac2u4ccft/blue.flashes.actor.portfolio, https://app.flashes.blue/profile/j4ck.xyz) 51 56 - room: flo-bit.dev/room
+101
src/lib/components/select-theme/SelectTheme.svelte
··· 1 + <script lang="ts"> 2 + import { Paragraph } from '@foxui/core'; 3 + import { ColorSelect } from '@foxui/colors'; 4 + 5 + let accentColors = [ 6 + { class: 'text-red-500', label: 'red' }, 7 + { class: 'text-orange-500', label: 'orange' }, 8 + { class: 'text-amber-500', label: 'amber' }, 9 + { class: 'text-yellow-500', label: 'yellow' }, 10 + { class: 'text-lime-500', label: 'lime' }, 11 + { class: 'text-green-500', label: 'green' }, 12 + { class: 'text-emerald-500', label: 'emerald' }, 13 + { class: 'text-teal-500', label: 'teal' }, 14 + { class: 'text-cyan-500', label: 'cyan' }, 15 + { class: 'text-sky-500', label: 'sky' }, 16 + { class: 'text-blue-500', label: 'blue' }, 17 + { class: 'text-indigo-500', label: 'indigo' }, 18 + { class: 'text-violet-500', label: 'violet' }, 19 + { class: 'text-purple-500', label: 'purple' }, 20 + { class: 'text-fuchsia-500', label: 'fuchsia' }, 21 + { class: 'text-pink-500', label: 'pink' }, 22 + { class: 'text-rose-500', label: 'rose' } 23 + ]; 24 + 25 + let baseColors = [ 26 + { class: 'text-gray-500', label: 'gray' }, 27 + { class: 'text-stone-500', label: 'stone' }, 28 + { class: 'text-zinc-500', label: 'zinc' }, 29 + { class: 'text-neutral-500', label: 'neutral' }, 30 + { class: 'text-slate-500', label: 'slate' } 31 + ]; 32 + 33 + let { 34 + accentColor = $bindable('pink'), 35 + baseColor = $bindable('stone'), 36 + selectAccentColor = true, 37 + selectBaseColor = true, 38 + onchanged 39 + }: { 40 + accentColor?: string; 41 + baseColor?: string; 42 + selectAccentColor?: boolean; 43 + selectBaseColor?: boolean; 44 + onchanged?: (accentColor: string, baseColor: string) => void; 45 + } = $props(); 46 + 47 + let selectedAccentColor = $derived( 48 + accentColors.find((c) => c.label === accentColor) ?? accentColors[15] 49 + ); 50 + 51 + let selectedBaseColor = $derived(baseColors.find((c) => c.label === baseColor) ?? baseColors[1]); 52 + </script> 53 + 54 + {#if selectAccentColor} 55 + <Paragraph class="mb-2">Accent Color</Paragraph> 56 + <ColorSelect 57 + selected={selectedAccentColor} 58 + colors={accentColors} 59 + onselected={(color, previous) => { 60 + if (typeof previous === 'string' || typeof color === 'string') { 61 + return; 62 + } 63 + 64 + document.documentElement.classList.remove(previous.label.toLowerCase()); 65 + document.documentElement.classList.add(color.label.toLowerCase()); 66 + 67 + accentColor = color.label; 68 + 69 + window.dispatchEvent( 70 + new CustomEvent('theme-changed', { detail: { accentColor: color.label } }) 71 + ); 72 + 73 + onchanged?.(accentColor, baseColor); 74 + }} 75 + class="w-64" 76 + /> 77 + {/if} 78 + 79 + {#if selectBaseColor} 80 + <Paragraph class="mt-4 mb-2">Base Color</Paragraph> 81 + <ColorSelect 82 + selected={selectedBaseColor} 83 + colors={baseColors} 84 + onselected={(color, previous) => { 85 + if (typeof previous === 'string' || typeof color === 'string') { 86 + return; 87 + } 88 + 89 + document.documentElement.classList.remove(previous.label.toLowerCase()); 90 + document.documentElement.classList.add(color.label.toLowerCase()); 91 + 92 + baseColor = color.label; 93 + 94 + window.dispatchEvent( 95 + new CustomEvent('theme-changed', { detail: { baseColor: color.label } }) 96 + ); 97 + 98 + onchanged?.(accentColor, baseColor); 99 + }} 100 + /> 101 + {/if}
+43
src/lib/components/select-theme/SelectThemePopover.svelte
··· 1 + <script lang="ts"> 2 + import { buttonVariants, Popover, cn } from '@foxui/core'; 3 + import SelectTheme from './SelectTheme.svelte'; 4 + 5 + let { 6 + accentColor = $bindable('pink'), 7 + baseColor = $bindable('stone'), 8 + selectAccentColor = true, 9 + selectBaseColor = true, 10 + onchanged 11 + }: { 12 + accentColor?: string; 13 + baseColor?: string; 14 + selectAccentColor?: boolean; 15 + selectBaseColor?: boolean; 16 + onchanged?: (accentColor: string, baseColor: string) => void; 17 + } = $props(); 18 + </script> 19 + 20 + <Popover> 21 + {#snippet child({ props })} 22 + <button 23 + {...props} 24 + class={cn( 25 + buttonVariants({ variant: 'link', size: 'default' }), 26 + 'flex cursor-pointer items-center gap-0 -space-x-2 backdrop-blur-none' 27 + )} 28 + > 29 + {#if selectAccentColor} 30 + <div 31 + class=" from-accent-500 to-accent-600 border-accent-700 dark:border-accent-400 z-10 size-6 rounded-full border bg-linear-to-b" 32 + ></div> 33 + {/if} 34 + 35 + {#if selectBaseColor} 36 + <div 37 + class=" from-base-500 to-base-600 border-base-700 dark:border-base-400 size-6 rounded-full border bg-linear-to-b" 38 + ></div> 39 + {/if} 40 + </button> 41 + {/snippet} 42 + <SelectTheme bind:accentColor bind:baseColor {selectAccentColor} {selectBaseColor} {onchanged} /> 43 + </Popover>
+2
src/lib/components/select-theme/index.ts
··· 1 + export { default as SelectTheme } from './SelectTheme.svelte'; 2 + export { default as SelectThemePopover } from './SelectThemePopover.svelte';
+4
src/lib/types.ts
··· 51 51 52 52 // 'side' (default on desktop) or 'top' (always top like mobile view) 53 53 profilePosition?: 'side' | 'top'; 54 + 55 + // theme colors 56 + accentColor?: string; 57 + baseColor?: string; 54 58 }; 55 59 }; 56 60 profile: AppBskyActorDefs.ProfileViewDetailed;
+18 -1
src/lib/website/EditableProfile.svelte
··· 5 5 import MarkdownTextEditor from '$lib/components/MarkdownTextEditor.svelte'; 6 6 import { Avatar, Button } from '@foxui/core'; 7 7 import { getIsMobile } from './context'; 8 - import type { Editor } from '@tiptap/core'; 9 8 import MadeWithBlento from './MadeWithBlento.svelte'; 9 + import { SelectThemePopover } from '$lib/components/select-theme'; 10 10 11 11 let { data = $bindable(), hideBlento = false }: { data: WebsiteData; hideBlento?: boolean } = 12 12 $props(); 13 + 14 + let accentColor = $derived(data.publication?.preferences?.accentColor ?? 'pink'); 15 + let baseColor = $derived(data.publication?.preferences?.baseColor ?? 'stone'); 16 + 17 + function updateTheme(newAccent: string, newBase: string) { 18 + data.publication.preferences ??= {}; 19 + data.publication.preferences.accentColor = newAccent; 20 + data.publication.preferences.baseColor = newBase; 21 + data = { ...data }; 22 + } 13 23 14 24 let profilePosition = $derived(getProfilePosition(data)); 15 25 ··· 130 140 {/if} 131 141 </Button> 132 142 {/if} 143 + 144 + <!-- Theme selection --> 145 + <SelectThemePopover 146 + {accentColor} 147 + {baseColor} 148 + onchanged={(newAccent, newBase) => updateTheme(newAccent, newBase)} 149 + /> 133 150 </div> 134 151 135 152 <div
+2
src/lib/website/EditableWebsite.svelte
··· 572 572 favicon={data.profile.avatar ?? null} 573 573 title={getName(data)} 574 574 image={'/' + data.handle + '/og.png'} 575 + accentColor={data.publication?.preferences?.accentColor} 576 + baseColor={data.publication?.preferences?.baseColor} 575 577 /> 576 578 577 579 <Account {data} />
+15 -3
src/lib/website/Head.svelte
··· 1 1 <script lang="ts"> 2 + import ThemeScript from './ThemeScript.svelte'; 3 + 2 4 let { 3 5 favicon, 4 6 title, 5 7 image, 6 - description 7 - }: { favicon: string | null; title: string | null; image?: string; description?: string } = 8 - $props(); 8 + description, 9 + accentColor, 10 + baseColor 11 + }: { 12 + favicon: string | null; 13 + title: string | null; 14 + image?: string; 15 + description?: string; 16 + accentColor?: string; 17 + baseColor?: string; 18 + } = $props(); 9 19 </script> 20 + 21 + <ThemeScript {accentColor} {baseColor} /> 10 22 11 23 <svelte:head> 12 24 {#if favicon}
+18
src/lib/website/ThemeScript.svelte
··· 1 + <script lang="ts"> 2 + let { 3 + accentColor = 'pink', 4 + baseColor = 'stone' 5 + }: { 6 + accentColor?: string; 7 + baseColor?: string; 8 + } = $props(); 9 + 10 + let script = $derived( 11 + `<script>(function(){document.documentElement.classList.add(${JSON.stringify(accentColor)},${JSON.stringify(baseColor)});})();<` + 12 + '/script>' 13 + ); 14 + </script> 15 + 16 + <svelte:head> 17 + {@html script} 18 + </svelte:head>
+2
src/lib/website/Website.svelte
··· 60 60 title={getName(data)} 61 61 image={'/' + data.handle + '/og.png'} 62 62 description={getDescription(data)} 63 + accentColor={data.publication?.preferences?.accentColor} 64 + baseColor={data.publication?.preferences?.baseColor} 63 65 /> 64 66 65 67 <Context {data}>