From 6422324eddf7d833d63627e81fa525faede98909 Mon Sep 17 00:00:00 2001 From: xarmian Date: Fri, 24 Jul 2026 15:04:48 -0400 Subject: [PATCH] =?UTF-8?q?feat(web):=20Button=20primitive=20+=20dark=20te?= =?UTF-8?q?xt-on-fill=20AA=20=E2=80=94=2095=20sites=20migrated=20(TASK-229?= =?UTF-8?q?2)=20(#1020)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * feat(web): Button primitive + dark text-on-fill AA fix; migrate 95 button sites (TASK-2292) PLAN-2290 Phase 2, PR 2. lib/components/common/Button.svelte — variants primary (filled --accent-primary-strong #7c4ff0, the violet band where white text passes AA 4.96:1 while staying >=3:1 vs surface — pays off the PR #1018 deferral) / secondary / ghost / danger (red tint, AA both themes); size sm/md; full attr passthrough (type=submit preserved at form sites). 95 usages across 14 files migrated (settings, conventions, playbooks x2, workspace home, console suite, modals, comment composer, EmptyState); dead scoped .btn* CSS deleted (net -291 lines). Deliberate leave-alones per file: ItemDetail action-bar strip (Phase 4 owns the pane), anchors styled as buttons, segmented controls, icon-only buttons, dashed low-emphasis affordances. Gates: svelte-check 0 errors (dead-selector warnings down 8->7), 488 web tests, make check green; screenshots reviewed both themes. * fix(web): Button class-prop merge + danger-solid variant for final confirms Codex findings on #1020: (1) caller-supplied class no longer clobbers the primitive's classes — class is destructured and merged, rest spread moved first; (2) new danger-solid variant (filled --accent-red-strong #dc2626, white text 4.83:1 AA both themes) restores destructive emphasis on the two final-confirm flows that had gone pale (OpenChildrenDialog override, conventions delete Confirm); entry-level destructive buttons keep the tint. --- web/src/app.css | 9 ++ .../lib/components/OpenChildrenDialog.svelte | 39 +----- .../components/admin/UserSettingsForm.svelte | 106 +++++----------- .../components/comments/CommentThread.svelte | 26 +--- web/src/lib/components/common/Button.svelte | 116 ++++++++++++++++++ .../lib/components/common/EmptyState.svelte | 25 ++-- .../layout/CreateWorkspaceModal.svelte | 28 +---- .../[username]/[workspace]/+page.svelte | 52 +------- .../[workspace]/conventions/+page.svelte | 50 ++++---- .../[workspace]/playbooks/+page.svelte | 61 ++++----- .../[workspace]/playbooks/[slug]/+page.svelte | 50 ++------ .../[workspace]/settings/+page.svelte | 58 ++++----- web/src/routes/console/+page.svelte | 22 +--- web/src/routes/console/billing/+page.svelte | 48 +++----- .../console/connected-apps/+page.svelte | 65 +++------- web/src/routes/console/settings/+page.svelte | 115 +++++++---------- 16 files changed, 349 insertions(+), 521 deletions(-) create mode 100644 web/src/lib/components/common/Button.svelte diff --git a/web/src/app.css b/web/src/app.css index da14190d..c09049f8 100644 --- a/web/src/app.css +++ b/web/src/app.css @@ -32,6 +32,10 @@ with the Phase 2 Button primitive via --text-on-accent. */ --accent-primary: #9268f8; --accent-primary-soft: #a78bfa; + /* Filled-control variant: the violet band where white text passes AA + (4.96:1) while the fill stays ≥3:1 against --bg-secondary. Used by + Button primary fills; links/text keep --accent-primary. */ + --accent-primary-strong: #7c4ff0; --accent-blue: var(--accent-primary); --accent-green: #4ade80; --accent-purple: #a78bfa; @@ -41,6 +45,9 @@ --accent-gray: #9ca3af; --accent-brown: #a8896c; --accent-red: #ef4444; + /* Solid destructive fill — the red where white text passes AA (4.83:1) + in both themes. Button variant="danger-solid" (final confirms). */ + --accent-red-strong: #dc2626; /* Literal blue for categorical uses (status maps, charts, info badges). Stays blue even if the brand accent changes — see PLAN-2290. */ @@ -651,6 +658,7 @@ dialog.attachment-image-lightbox .attachment-image-lightbox-close:hover { --chip-alpha: 12%; --accent-primary: #7c3aed; --accent-primary-soft: #8b5cf6; + --accent-primary-strong: #7c3aed; --accent-blue: var(--accent-primary); --accent-red: #dc2626; --accent-green: #16a34a; @@ -687,6 +695,7 @@ dialog.attachment-image-lightbox .attachment-image-lightbox-close:hover { --chip-alpha: 12%; --accent-primary: #7c3aed; --accent-primary-soft: #8b5cf6; + --accent-primary-strong: #7c3aed; --accent-blue: var(--accent-primary); --accent-red: #dc2626; --accent-green: #16a34a; diff --git a/web/src/lib/components/OpenChildrenDialog.svelte b/web/src/lib/components/OpenChildrenDialog.svelte index 996988bc..1b2829cc 100644 --- a/web/src/lib/components/OpenChildrenDialog.svelte +++ b/web/src/lib/components/OpenChildrenDialog.svelte @@ -15,6 +15,7 @@ import { page } from '$app/state'; import { openChildrenDialog } from '$lib/stores/openChildrenDialog.svelte'; import Modal from '$lib/components/common/Modal.svelte'; + import Button from '$lib/components/common/Button.svelte'; let active = $derived(openChildrenDialog.active); @@ -118,16 +119,10 @@ {/if} @@ -266,32 +261,6 @@ flex-shrink: 0; } - .btn { - padding: var(--space-2) var(--space-4); - border-radius: var(--radius-md); - font-size: 0.9em; - font-weight: 500; - cursor: pointer; - border: 1px solid transparent; - } - - .btn-secondary { - background: var(--bg-tertiary); - color: var(--text-primary); - border-color: var(--border); - } - .btn-secondary:hover { - background: var(--bg-secondary); - } - - .btn-danger { - background: var(--accent-red); - color: white; - } - .btn-danger:hover { - filter: brightness(1.08); - } - @media (max-width: 600px) { .modal-header, .modal-body, diff --git a/web/src/lib/components/admin/UserSettingsForm.svelte b/web/src/lib/components/admin/UserSettingsForm.svelte index 1c334d60..0aa0265d 100644 --- a/web/src/lib/components/admin/UserSettingsForm.svelte +++ b/web/src/lib/components/admin/UserSettingsForm.svelte @@ -21,6 +21,7 @@ import { untrack } from 'svelte'; import { adminFetch, adminPatch, adminPost, type AdminUser } from '$lib/stores/admin.svelte'; import { api } from '$lib/api/client'; + import Button from '$lib/components/common/Button.svelte'; interface Props { user: AdminUser; @@ -338,32 +339,29 @@ {#if editRole !== (user.role || 'member')} {#if !roleConfirm} - Change Role {:else}
{roleAction()} {user.name || user.username} to {editRole}? - {roleSaving ? 'Saving…' : 'Confirm'} - Cancel
{/if} @@ -388,17 +386,16 @@
{resetError}
{/if} {#if !resetConfirm} - + {:else}
Send a password-reset email or generate a temporary password? - {resetSaving ? 'Working…' : 'Confirm'} - +
{/if} @@ -409,14 +406,13 @@ {#if disableMsg}
{disableMsg}
{/if} {#if !user.disabled_at} {#if !disableConfirm} - Disable account {:else}