1
0
forked from wrenn/wrenn

Add per-provider brand colors to channels page

Give each provider (Discord, Slack, Teams, Google Chat, Telegram,
Matrix, Webhook) its own distinctive color for badges, row hover
stripes, and dialog tags. Move channel count into the header as a
serif numeral for stronger typographic hierarchy.
This commit is contained in:
2026-04-11 07:14:13 +06:00
parent 0807946d45
commit 430fb9e70e

View File

@ -94,6 +94,21 @@
return PROVIDERS.find((p) => p.value === value)?.label ?? value; return PROVIDERS.find((p) => p.value === value)?.label ?? value;
} }
// Per-provider color palette — [text, bg, border, stripe]
const PROVIDER_COLORS: Record<string, { text: string; bg: string; border: string }> = {
discord: { text: '#8b9cef', bg: 'rgba(88,101,242,0.12)', border: 'rgba(88,101,242,0.3)' },
slack: { text: '#d4a0c0', bg: 'rgba(180,120,160,0.10)', border: 'rgba(180,120,160,0.3)' },
teams: { text: '#a78bda', bg: 'rgba(120,90,200,0.10)', border: 'rgba(120,90,200,0.3)' },
googlechat: { text: '#6ec07a', bg: 'rgba(60,176,80,0.10)', border: 'rgba(60,176,80,0.25)' },
telegram: { text: '#6cb8d9', bg: 'rgba(42,171,226,0.10)', border: 'rgba(42,171,226,0.25)' },
matrix: { text: '#6bccc4', bg: 'rgba(80,200,190,0.10)', border: 'rgba(80,200,190,0.25)' },
webhook: { text: 'var(--color-text-secondary)', bg: 'rgba(255,255,255,0.04)', border: 'var(--color-border-mid)' },
};
function providerColor(provider: string): typeof PROVIDER_COLORS['discord'] {
return PROVIDER_COLORS[provider] ?? PROVIDER_COLORS['webhook'];
}
function fieldLabel(field: string): string { function fieldLabel(field: string): string {
return field.replace(/_/g, ' ').replace(/\b\w/g, (c) => c.toUpperCase()); return field.replace(/_/g, ' ').replace(/\b\w/g, (c) => c.toUpperCase());
} }
@ -338,13 +353,13 @@
<!-- Header --> <!-- Header -->
<div class="px-7 pt-8"> <div class="px-7 pt-8">
<div class="flex items-start justify-between"> <div class="flex items-start justify-between">
<div> <div class="flex items-baseline gap-4">
<h1 class="font-serif text-page tracking-[-0.02em] text-[var(--color-text-bright)]"> <h1 class="font-serif text-page tracking-[-0.02em] text-[var(--color-text-bright)]">
Channels Channels
</h1> </h1>
<p class="mt-2 text-ui text-[var(--color-text-secondary)]"> {#if !loading && channels.length > 0}
Route capsule events to Discord, Slack, Telegram, and other destinations. <span class="font-serif text-[1.75rem] tracking-[-0.03em] text-[var(--color-text-muted)]">{channels.length}</span>
</p> {/if}
</div> </div>
<button <button
@ -357,6 +372,9 @@
New Channel New Channel
</button> </button>
</div> </div>
<p class="mt-2 text-ui text-[var(--color-text-secondary)]">
Route capsule events to Discord, Slack, Telegram, and other destinations.
</p>
<div class="mt-5 border-b border-[var(--color-border)]"></div> <div class="mt-5 border-b border-[var(--color-border)]"></div>
</div> </div>
@ -432,13 +450,6 @@
</button> </button>
</div> </div>
{:else} {:else}
<!-- Count row -->
<div class="mb-4 flex items-center justify-end">
<span class="text-meta text-[var(--color-text-muted)]">
{channels.length} {channels.length === 1 ? 'channel' : 'channels'} total
</span>
</div>
<!-- Table --> <!-- Table -->
<div class="overflow-hidden rounded-[var(--radius-card)] border border-[var(--color-border)]"> <div class="overflow-hidden rounded-[var(--radius-card)] border border-[var(--color-border)]">
<!-- Header --> <!-- Header -->
@ -457,7 +468,7 @@
in:fly={{ y: 6, duration: 350, delay: i * 40, easing: cubicOut }} in:fly={{ y: 6, duration: 350, delay: i * 40, easing: cubicOut }}
out:fly={{ x: -12, duration: 180, easing: cubicIn }} out:fly={{ x: -12, duration: 180, easing: cubicIn }}
> >
<div class="row-stripe pointer-events-none absolute left-0 top-0 h-full w-[3px] bg-[var(--color-accent)]"></div> <div class="row-stripe pointer-events-none absolute left-0 top-0 h-full w-[3px]" style="background: {providerColor(ch.provider).text}"></div>
<!-- Name --> <!-- Name -->
<div class="min-w-0 px-5 py-4"> <div class="min-w-0 px-5 py-4">
@ -467,7 +478,10 @@
<!-- Provider --> <!-- Provider -->
<div class="px-5 py-4"> <div class="px-5 py-4">
<span class="inline-flex items-center gap-1.5 rounded-[3px] border border-[var(--color-accent)]/25 bg-[var(--color-accent)]/10 px-2.5 py-1 text-badge font-semibold uppercase tracking-[0.04em] text-[var(--color-accent-mid)]"> <span
class="inline-flex items-center gap-1.5 rounded-[3px] border px-2.5 py-1 text-badge font-semibold uppercase tracking-[0.04em]"
style="color: {providerColor(ch.provider).text}; background: {providerColor(ch.provider).bg}; border-color: {providerColor(ch.provider).border}"
>
{@render providerIcon(ch.provider)} {@render providerIcon(ch.provider)}
{providerLabel(ch.provider)} {providerLabel(ch.provider)}
</span> </span>
@ -687,9 +701,11 @@
style="animation: fadeUp 0.12s ease both" style="animation: fadeUp 0.12s ease both"
> >
{#each PROVIDERS as p} {#each PROVIDERS as p}
{@const ppc = providerColor(p.value)}
<button <button
class="flex w-full items-center gap-2.5 px-3 py-2 text-ui transition-colors duration-100 hover:bg-[var(--color-bg-3)] class="flex w-full items-center gap-2.5 px-3 py-2 text-ui transition-colors duration-100 hover:bg-[var(--color-bg-3)]
{createProvider === p.value ? 'bg-[var(--color-accent-glow)] text-[var(--color-accent-bright)] font-medium' : 'text-[var(--color-text-primary)]'}" {createProvider === p.value ? 'font-medium' : 'text-[var(--color-text-primary)]'}"
style={createProvider === p.value ? `color: ${ppc.text}; background: ${ppc.bg}` : ''}
onclick={() => { createProvider = p.value; createConfig = {}; providerDropdownOpen = false; }} onclick={() => { createProvider = p.value; createConfig = {}; providerDropdownOpen = false; }}
> >
{@render providerIcon(p.value)} {@render providerIcon(p.value)}
@ -987,7 +1003,7 @@
</p> </p>
<div class="mt-2"> <div class="mt-2">
<span class="inline-flex items-center gap-1.5 rounded-sm border border-[var(--color-border-mid)] bg-[var(--color-bg-4)] px-2 py-0.5 text-meta text-[var(--color-text-secondary)]"> <span class="inline-flex items-center gap-1.5 rounded-sm border px-2 py-0.5 text-meta font-medium" style="color: {providerColor(editTarget.provider).text}; background: {providerColor(editTarget.provider).bg}; border-color: {providerColor(editTarget.provider).border}">
{@render providerIcon(editTarget.provider)} {@render providerIcon(editTarget.provider)}
{providerLabel(editTarget.provider)} {providerLabel(editTarget.provider)}
</span> </span>
@ -1101,7 +1117,7 @@
Permanently delete <span class="font-medium text-[var(--color-text-secondary)]">{deleteTarget.name}</span>? Permanently delete <span class="font-medium text-[var(--color-text-secondary)]">{deleteTarget.name}</span>?
Events will stop being delivered to this destination immediately. Events will stop being delivered to this destination immediately.
</p> </p>
<span class="mt-2 inline-flex items-center gap-1.5 rounded-sm border border-[var(--color-border-mid)] bg-[var(--color-bg-4)] px-2 py-0.5 text-meta text-[var(--color-text-muted)]"> <span class="mt-2 inline-flex items-center gap-1.5 rounded-sm border px-2 py-0.5 text-meta font-medium" style="color: {providerColor(deleteTarget.provider).text}; background: {providerColor(deleteTarget.provider).bg}; border-color: {providerColor(deleteTarget.provider).border}">
{@render providerIcon(deleteTarget.provider)} {@render providerIcon(deleteTarget.provider)}
{providerLabel(deleteTarget.provider)} {providerLabel(deleteTarget.provider)}
</span> </span>
@ -1162,7 +1178,7 @@
</p> </p>
<div class="mt-2"> <div class="mt-2">
<span class="inline-flex items-center gap-1.5 rounded-sm border border-[var(--color-border-mid)] bg-[var(--color-bg-4)] px-2 py-0.5 text-meta text-[var(--color-text-secondary)]"> <span class="inline-flex items-center gap-1.5 rounded-sm border px-2 py-0.5 text-meta font-medium" style="color: {providerColor(rotateTarget.provider).text}; background: {providerColor(rotateTarget.provider).bg}; border-color: {providerColor(rotateTarget.provider).border}">
{@render providerIcon(rotateTarget.provider)} {@render providerIcon(rotateTarget.provider)}
{providerLabel(rotateTarget.provider)} {providerLabel(rotateTarget.provider)}
</span> </span>