Skip to content

Commit e0c33fe

Browse files
committed
Auto compute text color as contrast
1 parent ded5ed4 commit e0c33fe

4 files changed

Lines changed: 47 additions & 36 deletions

File tree

src/lib/utils/color.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,4 +7,12 @@ export function computeBrightness(hex: string): number {
77
const b = parseInt(c.substring(4,6),16);
88
// Perceived brightness formula
99
return (r * 299 + g * 587 + b * 114) / 1000;
10+
}
11+
12+
export function getContrastColor(hex: string): string {
13+
// Compute brightness
14+
const brightness = computeBrightness(hex);
15+
// Return black or white based on brightness
16+
// return brightness > 128 ? 'var(--primary)' : 'var(--primary-foreground)';
17+
return brightness > 128 ? 'hsl(240 5.9% 10%)' : 'hsl(0 0% 98%)';
1018
}

src/routes/(app)/+layout.svelte

Lines changed: 14 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -8,33 +8,38 @@
88
import * as Button from "$lib/components/ui/button";
99
import ModeSwitcher from "$lib/components/ModeSwitcher.svelte";
1010
import LanguageSwitcher from "$lib/components/LanguageSwitcher.svelte";
11+
import { getContrastColor } from "$lib/utils/color";
1112
1213
let { children } = $props();
1314
1415
let surveyId = $derived(page.url.searchParams.get('id'));
15-
let currentSurvey = $derived(surveyId ? $surveys.find(s => s.id === surveyId) : undefined);
16+
let survey = $derived(surveyId ? $surveys.find(s => s.id === surveyId) : undefined);
1617
1718
</script>
1819

1920

2021
<div
2122
class="min-h-screen w-screen flex flex-col items-center justify-center relative p-4 sm:p-8 bg-background text-foreground"
22-
style="background-color: {currentSurvey?.appearance.backgroundColor || ''};"
23+
style="background-color: {survey?.appearance.backgroundColor || ''};"
2324
>
2425
<!-- Top Controls: Overview Icon Left, Language Switcher Right, Theme Toggle -->
2526
<div class="absolute top-4 left-0 w-full flex justify-between items-center px-4 sm:top-6 z-10">
2627
<!-- Overview Icon (Left) -->
2728
<div>
28-
{#if page.url.pathname !== resolveRoute('/', {})}
29-
<Button.Root href={resolveRoute('/', {})} variant="secondary" size="icon" aria-label={m.navigate_home_aria_label() ? m.navigate_home_aria_label() : 'Manage Surveys'}>
30-
<List />
31-
</Button.Root>
29+
{#if survey}
30+
<Button.Root href={resolveRoute('/', {})} variant="ghost" size="icon" aria-label={m.navigate_home_aria_label() ? m.navigate_home_aria_label() : 'Manage Surveys'}>
31+
<List class="size-5 sm:size-6" color={getContrastColor(survey?.appearance.backgroundColor)}/>
32+
</Button.Root>
3233
{/if}
3334
</div>
3435
<!-- Right Controls: Language Switcher and Theme Toggle -->
35-
<div class="flex items-center gap-4">
36-
<LanguageSwitcher class="" />
37-
<ModeSwitcher variant="secondary" />
36+
<div>
37+
{#if !survey}
38+
<div class="flex items-center gap-4">
39+
<LanguageSwitcher class="" />
40+
<ModeSwitcher variant="secondary" />
41+
</div>
42+
{/if}
3843
</div>
3944
</div>
4045

src/routes/(app)/results/+page.svelte

Lines changed: 22 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -6,15 +6,15 @@
66
import { resolveRoute } from '$app/paths';
77
import * as Button from "$lib/components/ui/button";
88
import * as Progress from "$lib/components/ui/progress";
9-
import { computeBrightness } from "$lib/utils/color.js";
9+
import { getContrastColor } from "$lib/utils/color.js";
1010
1111
let surveyIdFromQuery = $derived(page.url.searchParams.get('id'));
12-
let currentSurvey = $derived(surveyIdFromQuery ? $surveys.find(s => s.id === surveyIdFromQuery) : undefined);
13-
let totalVotes = $derived(currentSurvey ? Object.values(currentSurvey.results).reduce((sum, count) => sum + count, 0) : 0);
12+
let survey = $derived(surveyIdFromQuery ? $surveys.find(s => s.id === surveyIdFromQuery) : undefined);
13+
let totalVotes = $derived(survey ? Object.values(survey.results).reduce((sum, count) => sum + count, 0) : 0);
1414
1515
function getAnswerDisplay(answerId: string): { text: string } {
16-
if (!currentSurvey) return { text: "Unknown Option" };
17-
const answer = currentSurvey.answers.find(a => a.id === answerId);
16+
if (!survey) return { text: "Unknown Option" };
17+
const answer = survey.answers.find(a => a.id === answerId);
1818
return answer
1919
? { text: answer.text || "Unknown" }
2020
: { text: "Unknown Option" };
@@ -24,30 +24,36 @@
2424
<title>{m.results_page_title()} | {m.app_title()}</title>
2525
</svelte:head>
2626

27-
{#if currentSurvey}
27+
{#if survey}
2828
<div class="w-full max-w-xl text-center">
29-
<h2 class="text-2xl sm:text-3xl font-bold mb-6">
30-
{currentSurvey.question}
29+
<h2 class="text-2xl sm:text-3xl font-bold mb-6" style="color: {getContrastColor(survey?.appearance.backgroundColor)};">
30+
{survey.question}
3131
</h2>
3232

3333
{#if totalVotes > 0}
3434
<div class="space-y-4 p-6">
35-
{#each currentSurvey.answers as answer (answer.id)}
36-
{@const votes = currentSurvey.results[answer.id] || 0}
35+
{#each survey.answers as answer (answer.id)}
36+
{@const votes = survey.results[answer.id] || 0}
3737
{@const percentage = totalVotes > 0 ? (votes / totalVotes) * 100 : 0}
3838
{@const display = getAnswerDisplay(answer.id)}
3939
<div class="text-left">
4040
<div class="flex justify-between items-center mb-1">
41-
<span class="text-md sm:text-lg">
41+
<span class="text-md sm:text-lg" style="color: {getContrastColor(survey?.appearance.backgroundColor)};">
4242
{display.text}: {votes} {votes === 1 ? m.votes_suffix_singular() : m.votes_suffix_plural()}
4343
</span>
44-
{#if percentage > 0}<span class="text-md sm:text-lg">{percentage.toFixed(1)}%</span>{/if}
44+
{#if percentage > 0}
45+
<span class="text-md sm:text-lg" style="color: {getContrastColor(survey?.appearance.backgroundColor)};">
46+
{percentage.toFixed(1)}%
47+
</span>
48+
{/if}
4549
</div>
46-
<Progress.Root value={percentage} class="h-6 sm:h-8" style="background-color: {currentSurvey?.appearance.buttonColor || ''};" />
50+
<Progress.Root value={percentage} class="h-6 sm:h-8" style="background-color: {survey?.appearance.buttonColor || ''};" />
4751
</div>
4852
{/each}
4953
</div>
50-
<p class="">{m.total_votes_label({ count: totalVotes })}</p>
54+
<p class="" style="color: {getContrastColor(survey?.appearance.backgroundColor)};">
55+
{m.total_votes_label({ count: totalVotes })}
56+
</p>
5157
{:else}
5258
<p class="text-xl text-muted-foreground my-8">{m.no_responses_message()}</p>
5359
{/if}
@@ -56,12 +62,8 @@
5662
onclick={() => goto(resolveRoute(`/survey?id=${surveyIdFromQuery}`, {}))}
5763
class="mt-8"
5864
style="
59-
background-color: {currentSurvey?.appearance.buttonColor || ''};
60-
color: {
61-
(() => {
62-
return computeBrightness(currentSurvey?.appearance.buttonColor) > 128 ? 'black' : 'white';
63-
})()
64-
};
65+
background-color: {survey?.appearance.buttonColor || ''};
66+
color: {getContrastColor(survey?.appearance.buttonColor)};
6567
"
6668
size="lg"
6769
>

src/routes/(app)/survey/+page.svelte

Lines changed: 3 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
import { m } from '$lib/paraglide/messages';
77
import { resolveRoute } from '$app/paths';
88
import * as Button from "$lib/components/ui/button";
9-
import { computeBrightness } from "$lib/utils/color.js";
9+
import { getContrastColor } from "$lib/utils/color.js";
1010
1111
// Get surveyId from query parameter 'id'
1212
let surveyIdFromQuery = $derived(page.url.searchParams.get('id'));
@@ -53,7 +53,7 @@
5353

5454
{#if survey}
5555
<div class="w-full max-w-lg text-center">
56-
<h1 class="text-3xl sm:text-4xl font-bold mb-8">
56+
<h1 class="text-3xl sm:text-4xl font-bold mb-8" style="color: {getContrastColor(survey?.appearance.backgroundColor)};">
5757
{currentSurveyQuestion}
5858
</h1>
5959

@@ -64,11 +64,7 @@
6464
class="grow px-6 min-w-30 "
6565
style="
6666
background-color: {survey?.appearance.buttonColor || ''};
67-
color: {
68-
(() => {
69-
return computeBrightness(survey?.appearance.buttonColor) > 128 ? 'black' : 'white';
70-
})()
71-
};
67+
color: {getContrastColor(survey?.appearance.buttonColor)};
7268
"
7369
size="lg"
7470
variant="default"

0 commit comments

Comments
 (0)