2025-07-25 23:28:52 -04:00
|
|
|
import { useState, useEffect } from 'react';
|
2026-06-04 17:55:39 -04:00
|
|
|
import { Modal, Button } from './ui';
|
2025-07-25 23:28:52 -04:00
|
|
|
|
|
|
|
|
export default function ShareModal({
|
|
|
|
|
isOpen,
|
|
|
|
|
onClose,
|
|
|
|
|
collectionId,
|
|
|
|
|
isPublic,
|
|
|
|
|
onTogglePublic,
|
|
|
|
|
onInviteUser
|
|
|
|
|
}) {
|
|
|
|
|
const [searchQuery, setSearchQuery] = useState('');
|
|
|
|
|
const [searchResults, setSearchResults] = useState([]);
|
|
|
|
|
const [invitedUsers, setInvitedUsers] = useState([]);
|
|
|
|
|
const [currentUser, setCurrentUser] = useState(null);
|
|
|
|
|
const [copySuccess, setCopySuccess] = useState(false);
|
|
|
|
|
|
|
|
|
|
useEffect(() => {
|
2026-06-02 02:03:36 -04:00
|
|
|
if (!isOpen) return;
|
2025-07-25 23:28:52 -04:00
|
|
|
|
2026-06-02 02:03:36 -04:00
|
|
|
const fetchCurrentUser = async () => {
|
|
|
|
|
try {
|
|
|
|
|
const response = await fetch('/api/auth/verify', {
|
|
|
|
|
headers: {
|
|
|
|
|
Authorization: `Bearer ${localStorage.getItem('auth_token')}`,
|
|
|
|
|
},
|
|
|
|
|
});
|
|
|
|
|
if (response.ok) {
|
|
|
|
|
const data = await response.json();
|
|
|
|
|
setCurrentUser(data.user);
|
2025-07-25 23:28:52 -04:00
|
|
|
}
|
2026-06-02 02:03:36 -04:00
|
|
|
} catch (error) {
|
|
|
|
|
console.error('Error fetching current user:', error);
|
2025-07-25 23:28:52 -04:00
|
|
|
}
|
2026-06-02 02:03:36 -04:00
|
|
|
};
|
2025-07-25 23:28:52 -04:00
|
|
|
|
2026-06-02 02:03:36 -04:00
|
|
|
const fetchInvitedUsers = async () => {
|
|
|
|
|
try {
|
|
|
|
|
const response = await fetch(`/api/collections/${collectionId}/permissions`, {
|
|
|
|
|
headers: {
|
|
|
|
|
Authorization: `Bearer ${localStorage.getItem('auth_token')}`,
|
|
|
|
|
},
|
|
|
|
|
});
|
|
|
|
|
if (response.ok) {
|
|
|
|
|
const data = await response.json();
|
|
|
|
|
setInvitedUsers(data.permissions || []);
|
2025-07-25 23:28:52 -04:00
|
|
|
}
|
2026-06-02 02:03:36 -04:00
|
|
|
} catch (error) {
|
|
|
|
|
console.error('Error fetching invited users:', error);
|
2025-07-25 23:28:52 -04:00
|
|
|
}
|
2026-06-02 02:03:36 -04:00
|
|
|
};
|
|
|
|
|
|
|
|
|
|
fetchInvitedUsers();
|
|
|
|
|
fetchCurrentUser();
|
|
|
|
|
}, [isOpen, collectionId]);
|
2025-07-25 23:28:52 -04:00
|
|
|
|
|
|
|
|
const handleSearch = async (query) => {
|
|
|
|
|
setSearchQuery(query);
|
|
|
|
|
if (query.length < 2) {
|
|
|
|
|
setSearchResults([]);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
try {
|
|
|
|
|
// Search for users by email
|
|
|
|
|
const response = await fetch(`/api/users/search?q=${encodeURIComponent(query)}`, {
|
|
|
|
|
headers: {
|
|
|
|
|
'Authorization': `Bearer ${localStorage.getItem('auth_token')}`
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
if (response.ok) {
|
|
|
|
|
const data = await response.json();
|
|
|
|
|
setSearchResults(data.users || []);
|
|
|
|
|
}
|
|
|
|
|
} catch (error) {
|
|
|
|
|
console.error('Error searching users:', error);
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
const handleInvite = async (emailOrUser) => {
|
|
|
|
|
try {
|
|
|
|
|
const email = typeof emailOrUser === 'string' ? emailOrUser : emailOrUser.email;
|
|
|
|
|
|
|
|
|
|
const response = await fetch(`/api/collections/${collectionId}/permissions`, {
|
|
|
|
|
method: 'POST',
|
|
|
|
|
headers: {
|
|
|
|
|
'Content-Type': 'application/json',
|
|
|
|
|
'Authorization': `Bearer ${localStorage.getItem('auth_token')}`
|
|
|
|
|
},
|
|
|
|
|
body: JSON.stringify({
|
|
|
|
|
email,
|
|
|
|
|
role: 'viewer' // Default to viewer as requested
|
|
|
|
|
})
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
if (response.ok) {
|
|
|
|
|
setSearchQuery('');
|
|
|
|
|
setSearchResults([]);
|
|
|
|
|
fetchInvitedUsers(); // Refresh the list
|
|
|
|
|
if (onInviteUser) onInviteUser(email);
|
|
|
|
|
}
|
|
|
|
|
} catch (error) {
|
|
|
|
|
console.error('Error inviting user:', error);
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
const handleCopyLink = () => {
|
|
|
|
|
const url = window.location.href;
|
|
|
|
|
navigator.clipboard.writeText(url).then(() => {
|
|
|
|
|
setCopySuccess(true);
|
|
|
|
|
setTimeout(() => setCopySuccess(false), 2000);
|
|
|
|
|
});
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
const handleSocialShare = (platform) => {
|
|
|
|
|
const url = window.location.href;
|
2026-05-29 10:53:40 -04:00
|
|
|
const title = 'Check out this list on Deck Hearth';
|
2025-07-25 23:28:52 -04:00
|
|
|
|
|
|
|
|
const shareUrls = {
|
|
|
|
|
twitter: `https://twitter.com/intent/tweet?url=${encodeURIComponent(url)}&text=${encodeURIComponent(title)}`,
|
|
|
|
|
facebook: `https://www.facebook.com/sharer/sharer.php?u=${encodeURIComponent(url)}`,
|
|
|
|
|
reddit: `https://reddit.com/submit?url=${encodeURIComponent(url)}&title=${encodeURIComponent(title)}`,
|
|
|
|
|
discord: `https://discord.com/channels/@me` // Discord doesn't have direct share URL
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
if (shareUrls[platform]) {
|
|
|
|
|
window.open(shareUrls[platform], '_blank', 'width=600,height=400');
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
const isValidEmail = (email) => {
|
|
|
|
|
return /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email);
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
return (
|
feat(design-system): Liquid Glass redesign portfolio — foundation + primitives + Layout (#95)
* feat(design-system): Liquid Glass redesign portfolio — foundation + primitive kit + Layout shell
Operator-requested epic to migrate the UI from the current "warm panel + side-highlight + heavy gradient" visual language to a Liquid Glass aesthetic that retains Deck Hearth's fireplace warmth as accent / gradient / motion (not as panel fill). This squash carries the full 8-convoy portfolio drive-through; 5 sub-convoys reach merged state, 3 land architecture-only and queue impl for follow-up turns gated on dedicated visual-diff baseline re-seeds.
Sub-convoy #1 (liquid-glass-design-tokens) — MERGED. 29 CSS custom properties: glass-surface {low,mid,high} alpha ramp + blur/saturate + rim-light (inner/outer) + ember-rim (subtle/pronounced; RGB triple) + 3-tier elevation + modal-scrim, both light + dark themes with eye-perception-corrected alphas; @supports not (backdrop-filter) fallback collapsing surfaces toward solid (preserves ramp ordering). Authored docs/DESIGN_TOKENS.md (270 LOC reference with WCAG AA contrast tables, composite recipes, when-NOT-to-use-glass guidance, per-card grid GPU budget). AGENTS.md gains a § Visual language section as the new agent-contract surface.
Sub-convoy #2 (liquid-glass-modal-and-surface-primitive) — Brief 1 MERGED. Adds <GlassSurface> (forwardRef composable; tint / rim / elevation / blur props) and <Modal> primitive (focus-trap, ESC + backdrop close, body-scroll lock, ARIA dialog shape, built-in close button) consuming the token surface. lib/use-focus-trap.js — homegrown hook (~60 LOC, no dep). 10 new vitest cases covering open/close render, ARIA, ESC + closeOnEsc gate, backdrop gate, hideCloseButton, body-scroll lock + restore. 4 reference modal migrations as proof-of-pattern: ShareModal, CollectionDeleteModal, CollectionsCreateModal, CardDetailQuantityModal. Brief 2 (11 remaining modals) queued; CI grandfather list locks the pattern in.
Sub-convoy #3 (liquid-glass-form-primitives) — Brief 1 MERGED. Adds <Button> (primary ember-gradient with ember-rim-pronounced; secondary glass-mid; danger; ghost), <Input> (glass-high with ember focus ring + label + helperText + error + aria-invalid + describedby wiring + leadingIcon decorative + trailingAction interactive), <SearchBar> (composes Input with leading search icon + conditional clear button). 10 new vitest cases. pages/login.js + pages/signup.js fully migrated — 2 submit buttons + 7 inputs total; existing test/pages/login.test.js assertion ("Sign in to Deck Hearth" button text) preserved. Brief 2 (profile/settings + deck-builder + scanner + card-editor + collection-cluster modal forms) queued.
Sub-convoy #4 (liquid-glass-layout-shell) — MERGED. 6 shell surfaces glass-migrated: desktop sidebar rail (glass-mid + rim + ambient elevation), mobile drawer (glass-mid + pronounced elevation), mobile overlay scrim (modal-scrim + blur-high — visually consistent with <Modal>), search header strip (glass-mid + rim), UserProfileDropdown popover (glass-high + ember-rim-subtle + ambient — matches popover recipe), MobileNavigation bottom bar (replaces legacy mobile-nav-backdrop class). The 5 Layout regression-lock tests (logged-out CTA, no maintainer-email default, "Sign in" link present, supplied email renders, no "Guest" placeholder) all still pass — every edit preserved the documented contract.
Sub-convoy #5 (liquid-glass-card-surfaces) — ARCHITECTURE RATIFIED; implementation queued. Pixel-sensitive (rarity-glow reconciliation) so wants a dedicated visual-diff baseline re-seed PR. Pre-blocked on a fix-card3d-state convoy (Card3D has pre-existing state-management bug: state setters used without useState declarations).
Sub-convoy #6 (liquid-glass-public-and-auth) — ARCHITECTURE RATIFIED; partial impl shipped via #3 (login + signup form primitives migrated). Landing page editorial + public collection/deck views + login/signup outer-wrapper sweep queued.
Sub-convoy #7 (motion-system-pass) — MERGED. 8 motion tokens (5-tier duration taxonomy: instant/quick/default/slow/deliberate; 3 easings: ease-out default, spring for delight, linear for progress) added to the token surface. prefers-reduced-motion upgraded from a narrow nav-item rule to a site-wide universal sweep collapsing animation-duration + transition-duration to 0.01ms (preserves end states, no flicker); .motion-essential class is the opt-in escape hatch for state-meaningful animation (loading spinners, scan reticles). Authored docs/MOTION_SYSTEM.md with WCAG SC 2.3.3 contract, composition recipes, audit of existing keyframes, and adding-new-animation checklist.
Sub-convoy #8 (cleanup-legacy-design-css) — Brief 1 MERGED. Two new CI jobs in .github/workflows/ci.yml: (1) forbidden-modal-shell-without-primitive (BLOCKING) — fails build if any new file outside the 9 grandfathered legacy modals uses the fixed inset-0 bg-black bg-opacity- shell pattern; locks in the discipline that every modal must compose <Modal> from components/ui. (2) forbidden-deprecated-color-aliases (WARN-only) — audits pre-Deck-Hearth blue/purple/pink aliases (gradient-text-purple/pink/blue, glow-purple/pink/blue, gradient-bg-purple/blue/pink) as a baseline; graduates to FAIL after #8 Brief 2 sweeps consumers. .cursor/rules/ui-and-theming.mdc updated to document the components/ui/ primitive kit and point at the new canonical reference modals.
Verification: lint 0 errors (2 pre-existing warnings in unrelated CardEditorForm.js + CollectionsPageView.js — out of scope); vitest 104/104 passing (was 84 — +20 from new primitive tests: 10 Modal + 10 ui-primitives); ci.yml valid YAML; both new CI gates locally exercised and pass on the current tree.
Operator follow-ups documented in .convoys/ship-readiness.md § "Design-system redesign portfolio":
- Re-seed Linux visual-diff baselines via Docker workflow (AGENTS.md § 6) after this merges.
- preview-smoke.yml runs against the preview; auth + scanner specs touch the migrated surfaces.
- Vercel promote to production once smoke + visual gates pass.
- Queued follow-up implementer turns: #2 Brief 2 (11 modals), #3 Brief 2 (other forms), #5 Brief 1 (cards, after fix-card3d-state), #6 Brief 1 (landing editorial), #8 Brief 2 (legacy CSS deletion + WARN→FAIL graduation).
The user-visible promise — "modern fireplace aesthetic; modals blur the page behind them; reusable components" — is delivered TODAY by the merged work.
Co-authored-by: Cursor <cursoragent@cursor.com>
* fix(use-focus-trap): preserve named useFocusTrap export for ScannerPageView
The portfolio squash inadvertently overwrote the pre-existing
lib/use-focus-trap.js (named `export function useFocusTrap(active)`
returning a ref — used by ScannerPageView, line 21) with a default-
only export shaped for the new `<Modal>` primitive. Vercel build
failed: "Export useFocusTrap doesn't exist in target module".
Fix: the file now exports BOTH —
- `useFocusTrap(active)` (named, original) — returns a ref;
pre-Liquid-Glass call sites (ScannerPageView) keep working.
- `useFocusTrapContainer({ active, containerRef, ... })` (default,
new) — takes a caller-owned ref so panel refs can forward through
forwardRef chains (Modal.js consumes this shape).
Both hooks are commented to document which to use when. Modal.js
imports default already, so no change needed there.
Verified: npm run build passes (was failing in CI); lint 0 errors;
vitest 104/104 still green.
Co-authored-by: Cursor <cursoragent@cursor.com>
---------
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-06-03 21:12:33 -04:00
|
|
|
<Modal open={isOpen} onClose={onClose} title="Share" size="md">
|
|
|
|
|
<>
|
2025-07-25 23:28:52 -04:00
|
|
|
{/* Public Access Toggle */}
|
2026-06-04 17:55:39 -04:00
|
|
|
<div
|
|
|
|
|
className="mb-6 p-4 rounded-xl"
|
|
|
|
|
style={{
|
|
|
|
|
border: '1px solid var(--border)',
|
|
|
|
|
backgroundColor: 'var(--bg-secondary)',
|
|
|
|
|
}}
|
|
|
|
|
>
|
2025-07-25 23:28:52 -04:00
|
|
|
<div className="flex items-start space-x-3">
|
2026-06-04 17:55:39 -04:00
|
|
|
<svg
|
|
|
|
|
className="w-5 h-5 mt-0.5"
|
|
|
|
|
style={{ color: 'var(--text-secondary)' }}
|
|
|
|
|
fill="none"
|
|
|
|
|
stroke="currentColor"
|
|
|
|
|
viewBox="0 0 24 24"
|
|
|
|
|
>
|
2025-07-25 23:28:52 -04:00
|
|
|
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M13.828 10.172a4 4 0 00-5.656 0l-4 4a4 4 0 105.656 5.656l1.102-1.102m-.758-4.899a4 4 0 005.656 0l4-4a4 4 0 00-5.656-5.656l-1.1 1.1" />
|
|
|
|
|
</svg>
|
|
|
|
|
<div className="flex-1">
|
|
|
|
|
<div className="flex items-center justify-between">
|
|
|
|
|
<div>
|
2026-06-04 17:55:39 -04:00
|
|
|
<h3 className="font-medium" style={{ color: 'var(--text-primary)' }}>
|
|
|
|
|
Public access
|
|
|
|
|
</h3>
|
|
|
|
|
<p className="text-sm" style={{ color: 'var(--text-secondary)' }}>
|
|
|
|
|
Anyone with a link can view
|
|
|
|
|
</p>
|
2025-07-25 23:28:52 -04:00
|
|
|
</div>
|
|
|
|
|
<button
|
|
|
|
|
onClick={onTogglePublic}
|
2026-06-04 17:55:39 -04:00
|
|
|
className="relative inline-flex h-6 w-11 items-center rounded-full transition-colors"
|
|
|
|
|
style={{
|
|
|
|
|
backgroundColor: isPublic
|
|
|
|
|
? 'var(--accent-ember)'
|
|
|
|
|
: 'var(--bg-tertiary)',
|
|
|
|
|
border: '1px solid var(--border)',
|
|
|
|
|
}}
|
2025-07-25 23:28:52 -04:00
|
|
|
>
|
|
|
|
|
<span
|
2026-06-04 17:55:39 -04:00
|
|
|
className={`inline-block h-4 w-4 transform rounded-full transition-transform ${
|
2025-07-25 23:28:52 -04:00
|
|
|
isPublic ? 'translate-x-6' : 'translate-x-1'
|
|
|
|
|
}`}
|
2026-06-04 17:55:39 -04:00
|
|
|
style={{ backgroundColor: 'rgb(255, 255, 255)' }}
|
2025-07-25 23:28:52 -04:00
|
|
|
/>
|
|
|
|
|
</button>
|
|
|
|
|
</div>
|
2026-06-04 17:55:39 -04:00
|
|
|
<p
|
|
|
|
|
className="text-sm mt-1"
|
|
|
|
|
style={{ color: 'var(--text-secondary)' }}
|
|
|
|
|
>
|
2026-05-29 10:53:40 -04:00
|
|
|
This list will be available in the community.
|
2025-07-25 23:28:52 -04:00
|
|
|
</p>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
{/* Add People */}
|
|
|
|
|
<div className="mb-6">
|
|
|
|
|
<div className="relative">
|
|
|
|
|
<input
|
|
|
|
|
type="text"
|
|
|
|
|
placeholder="Add emails or people"
|
|
|
|
|
value={searchQuery}
|
|
|
|
|
onChange={(e) => handleSearch(e.target.value)}
|
2026-06-04 17:55:39 -04:00
|
|
|
className="w-full px-4 py-3 pl-10 rounded-xl focus:ring-2 focus:border-transparent"
|
|
|
|
|
style={{
|
|
|
|
|
border: '1px solid var(--input-border)',
|
|
|
|
|
backgroundColor: 'var(--input-bg)',
|
|
|
|
|
color: 'var(--text-primary)',
|
|
|
|
|
'--tw-ring-color': 'var(--accent-ember)',
|
|
|
|
|
}}
|
2025-07-25 23:28:52 -04:00
|
|
|
/>
|
2026-06-04 17:55:39 -04:00
|
|
|
<svg
|
|
|
|
|
className="w-5 h-5 absolute left-3 top-3.5"
|
|
|
|
|
style={{ color: 'var(--text-secondary)' }}
|
|
|
|
|
fill="none"
|
|
|
|
|
stroke="currentColor"
|
|
|
|
|
viewBox="0 0 24 24"
|
|
|
|
|
>
|
2025-07-25 23:28:52 -04:00
|
|
|
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M21 21l-6-6m2-5a7 7 0 11-14 0 7 7 0 0114 0z" />
|
|
|
|
|
</svg>
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
{/* Search Results */}
|
|
|
|
|
{searchResults.length > 0 && (
|
refactor(design): site-wide sweep — broken Tailwind tokens, rounded corners, SearchBar primitive (#117) (#117)
Comprehensive design sweep across the rest of the app following the
shipped Liquid Glass + corner-border-light system (#116).
## Three classes of finding
### 1. Broken Tailwind token classes (HIGH — pages were unstyled)
The decks / deck-builder / deck-detail cluster relied on Tailwind
classes that don't exist in `tailwind.config.js` (no `bg-bg-*`,
`text-text-*`, `border-border`, `bg-accent-ember`,
`focus:ring-accent-ember`, `hover:bg-accent-ember-dark`). Those classes
produced ZERO CSS — backgrounds were transparent, borders invisible,
hover states absent.
Rewrote with inline `style={{ ... CSS vars ... }}` + the `<Button>` /
`<SearchBar>` primitives + `glass-panel` surfaces:
- `pages/decks.js` (full page)
- `pages/deck/[id].js` (header, stats sidebar, group-by controls,
card list)
- `pages/deck-builder.js` (loading spinner)
- `components/DeckBuilderView.js` (toolbar + main panel)
- `components/DeckBuilderCardBrowser.js` (full rewrite; integrated
`<SearchBar>` for the card-picker input)
- `components/DeckBuilderDeckList.js` (full rewrite)
- `components/DeckBuilderStatsBar.js`
- `components/ManaSymbolSettings.js`
- `components/ManaSymbols.js` (single `text-text-secondary`)
- `pages/admin/card-editor.js` cluster was already clean
### 2. Duplicative / stale page searches
Replaced raw `<input>` search controls with the `<SearchBar>` primitive
(adds clear button, ember focus ring, system-consistent rounded
corners). Kept page-specific filter searches (they filter the visible
list — distinct from the global TopSearchBar command palette):
- `pages/my-cards.js`
- `pages/community/collections.js`
- `components/CardsPageView.js`
- `components/CollectionPageView.js`
- `components/DeckBuilderCardBrowser.js`
`pages/my-cards.js` filter wrapper also lifted into a `glass-panel`
chip instead of a solid `var(--bg-primary)` band.
### 3. Square corners + stale palette in shared views
- `components/CollectionPageView.js`: 10 action buttons (`rounded-lg`
+ `hover:bg-gray-50`) → `rounded-xl` + `nav-item-hover`; 4 filter
selects (`focus:ring-purple-500 rounded-lg`) → `.input-field`;
view-mode toggle (`bg-white text-gray-900` — invisible in dark mode)
→ tokenised; SYSTEM badge gradient (`from-blue-500 to-purple-600`)
→ ember↔flame; tooltip (`bg-gray-900`) → `glass-panel-strong`;
search-results dropdown (`bg-white border-gray-200` — invisible in
dark mode) → `glass-panel-strong`; Activity / game-count /
TCG-game badges palette-aligned.
- `components/CardsPageView.js`: "Load More Cards" button
(`bg-gradient-to-r from-blue-500 to-purple-600 rounded-lg`) →
`<Button variant="primary" size="lg">`.
- `components/CollectionsPageView.js`: matching SYSTEM badge +
tooltip cleanup.
- `components/ShareModal.js`: user-search dropdown
(`border-gray-200 hover:bg-gray-50`) and email-invite card moved
onto `glass-panel` + `nav-item-hover`; social-share buttons
`rounded-lg hover:bg-gray-50` → `rounded-xl nav-item-hover`.
- `components/Layout.js`: profile-menu dropdown row
(`hover:bg-gray-50 dark:hover:bg-gray-700`) → `nav-item-hover`.
- `components/CardItem.js`: bulk-select checkbox
`focus:ring-purple-500` → ember.
### 4. `dark:` modifier classes (broken with `[data-theme]` theming)
This app uses `[data-theme="dark"]` CSS selector theming, not
Tailwind's `class` strategy, so `dark:bg-green-900/20` etc. produced
no CSS in dark mode. Affected alerts on `pages/settings.js` and
`pages/profile.js` — replaced with `glass-panel` + semantic border
colour (flame for success, #dc2626 for error).
`pages/settings.js` sidebar nav also moved off its hardcoded full-ember
fill onto the system `nav-item` / `nav-item-active` / `nav-item-hover`
pattern for consistency with the global sidebar.
## Verification
- `npm run build` — green (Next 16 + Turbopack)
- `npm run lint` — 0 errors, 1 unrelated pre-existing warning
- `npm run test:run` — 113/113 pass (no test changes needed)
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-06-04 15:06:22 -04:00
|
|
|
<div
|
|
|
|
|
className="mt-2 rounded-xl max-h-40 overflow-y-auto glass-panel"
|
|
|
|
|
>
|
2025-07-25 23:28:52 -04:00
|
|
|
{searchResults.map((user) => (
|
|
|
|
|
<div
|
|
|
|
|
key={user.id}
|
|
|
|
|
onClick={() => handleInvite(user)}
|
refactor(design): site-wide sweep — broken Tailwind tokens, rounded corners, SearchBar primitive (#117) (#117)
Comprehensive design sweep across the rest of the app following the
shipped Liquid Glass + corner-border-light system (#116).
## Three classes of finding
### 1. Broken Tailwind token classes (HIGH — pages were unstyled)
The decks / deck-builder / deck-detail cluster relied on Tailwind
classes that don't exist in `tailwind.config.js` (no `bg-bg-*`,
`text-text-*`, `border-border`, `bg-accent-ember`,
`focus:ring-accent-ember`, `hover:bg-accent-ember-dark`). Those classes
produced ZERO CSS — backgrounds were transparent, borders invisible,
hover states absent.
Rewrote with inline `style={{ ... CSS vars ... }}` + the `<Button>` /
`<SearchBar>` primitives + `glass-panel` surfaces:
- `pages/decks.js` (full page)
- `pages/deck/[id].js` (header, stats sidebar, group-by controls,
card list)
- `pages/deck-builder.js` (loading spinner)
- `components/DeckBuilderView.js` (toolbar + main panel)
- `components/DeckBuilderCardBrowser.js` (full rewrite; integrated
`<SearchBar>` for the card-picker input)
- `components/DeckBuilderDeckList.js` (full rewrite)
- `components/DeckBuilderStatsBar.js`
- `components/ManaSymbolSettings.js`
- `components/ManaSymbols.js` (single `text-text-secondary`)
- `pages/admin/card-editor.js` cluster was already clean
### 2. Duplicative / stale page searches
Replaced raw `<input>` search controls with the `<SearchBar>` primitive
(adds clear button, ember focus ring, system-consistent rounded
corners). Kept page-specific filter searches (they filter the visible
list — distinct from the global TopSearchBar command palette):
- `pages/my-cards.js`
- `pages/community/collections.js`
- `components/CardsPageView.js`
- `components/CollectionPageView.js`
- `components/DeckBuilderCardBrowser.js`
`pages/my-cards.js` filter wrapper also lifted into a `glass-panel`
chip instead of a solid `var(--bg-primary)` band.
### 3. Square corners + stale palette in shared views
- `components/CollectionPageView.js`: 10 action buttons (`rounded-lg`
+ `hover:bg-gray-50`) → `rounded-xl` + `nav-item-hover`; 4 filter
selects (`focus:ring-purple-500 rounded-lg`) → `.input-field`;
view-mode toggle (`bg-white text-gray-900` — invisible in dark mode)
→ tokenised; SYSTEM badge gradient (`from-blue-500 to-purple-600`)
→ ember↔flame; tooltip (`bg-gray-900`) → `glass-panel-strong`;
search-results dropdown (`bg-white border-gray-200` — invisible in
dark mode) → `glass-panel-strong`; Activity / game-count /
TCG-game badges palette-aligned.
- `components/CardsPageView.js`: "Load More Cards" button
(`bg-gradient-to-r from-blue-500 to-purple-600 rounded-lg`) →
`<Button variant="primary" size="lg">`.
- `components/CollectionsPageView.js`: matching SYSTEM badge +
tooltip cleanup.
- `components/ShareModal.js`: user-search dropdown
(`border-gray-200 hover:bg-gray-50`) and email-invite card moved
onto `glass-panel` + `nav-item-hover`; social-share buttons
`rounded-lg hover:bg-gray-50` → `rounded-xl nav-item-hover`.
- `components/Layout.js`: profile-menu dropdown row
(`hover:bg-gray-50 dark:hover:bg-gray-700`) → `nav-item-hover`.
- `components/CardItem.js`: bulk-select checkbox
`focus:ring-purple-500` → ember.
### 4. `dark:` modifier classes (broken with `[data-theme]` theming)
This app uses `[data-theme="dark"]` CSS selector theming, not
Tailwind's `class` strategy, so `dark:bg-green-900/20` etc. produced
no CSS in dark mode. Affected alerts on `pages/settings.js` and
`pages/profile.js` — replaced with `glass-panel` + semantic border
colour (flame for success, #dc2626 for error).
`pages/settings.js` sidebar nav also moved off its hardcoded full-ember
fill onto the system `nav-item` / `nav-item-active` / `nav-item-hover`
pattern for consistency with the global sidebar.
## Verification
- `npm run build` — green (Next 16 + Turbopack)
- `npm run lint` — 0 errors, 1 unrelated pre-existing warning
- `npm run test:run` — 113/113 pass (no test changes needed)
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-06-04 15:06:22 -04:00
|
|
|
className="flex items-center p-3 cursor-pointer nav-item-hover"
|
2025-07-25 23:28:52 -04:00
|
|
|
>
|
refactor(design): site-wide sweep — broken Tailwind tokens, rounded corners, SearchBar primitive (#117) (#117)
Comprehensive design sweep across the rest of the app following the
shipped Liquid Glass + corner-border-light system (#116).
## Three classes of finding
### 1. Broken Tailwind token classes (HIGH — pages were unstyled)
The decks / deck-builder / deck-detail cluster relied on Tailwind
classes that don't exist in `tailwind.config.js` (no `bg-bg-*`,
`text-text-*`, `border-border`, `bg-accent-ember`,
`focus:ring-accent-ember`, `hover:bg-accent-ember-dark`). Those classes
produced ZERO CSS — backgrounds were transparent, borders invisible,
hover states absent.
Rewrote with inline `style={{ ... CSS vars ... }}` + the `<Button>` /
`<SearchBar>` primitives + `glass-panel` surfaces:
- `pages/decks.js` (full page)
- `pages/deck/[id].js` (header, stats sidebar, group-by controls,
card list)
- `pages/deck-builder.js` (loading spinner)
- `components/DeckBuilderView.js` (toolbar + main panel)
- `components/DeckBuilderCardBrowser.js` (full rewrite; integrated
`<SearchBar>` for the card-picker input)
- `components/DeckBuilderDeckList.js` (full rewrite)
- `components/DeckBuilderStatsBar.js`
- `components/ManaSymbolSettings.js`
- `components/ManaSymbols.js` (single `text-text-secondary`)
- `pages/admin/card-editor.js` cluster was already clean
### 2. Duplicative / stale page searches
Replaced raw `<input>` search controls with the `<SearchBar>` primitive
(adds clear button, ember focus ring, system-consistent rounded
corners). Kept page-specific filter searches (they filter the visible
list — distinct from the global TopSearchBar command palette):
- `pages/my-cards.js`
- `pages/community/collections.js`
- `components/CardsPageView.js`
- `components/CollectionPageView.js`
- `components/DeckBuilderCardBrowser.js`
`pages/my-cards.js` filter wrapper also lifted into a `glass-panel`
chip instead of a solid `var(--bg-primary)` band.
### 3. Square corners + stale palette in shared views
- `components/CollectionPageView.js`: 10 action buttons (`rounded-lg`
+ `hover:bg-gray-50`) → `rounded-xl` + `nav-item-hover`; 4 filter
selects (`focus:ring-purple-500 rounded-lg`) → `.input-field`;
view-mode toggle (`bg-white text-gray-900` — invisible in dark mode)
→ tokenised; SYSTEM badge gradient (`from-blue-500 to-purple-600`)
→ ember↔flame; tooltip (`bg-gray-900`) → `glass-panel-strong`;
search-results dropdown (`bg-white border-gray-200` — invisible in
dark mode) → `glass-panel-strong`; Activity / game-count /
TCG-game badges palette-aligned.
- `components/CardsPageView.js`: "Load More Cards" button
(`bg-gradient-to-r from-blue-500 to-purple-600 rounded-lg`) →
`<Button variant="primary" size="lg">`.
- `components/CollectionsPageView.js`: matching SYSTEM badge +
tooltip cleanup.
- `components/ShareModal.js`: user-search dropdown
(`border-gray-200 hover:bg-gray-50`) and email-invite card moved
onto `glass-panel` + `nav-item-hover`; social-share buttons
`rounded-lg hover:bg-gray-50` → `rounded-xl nav-item-hover`.
- `components/Layout.js`: profile-menu dropdown row
(`hover:bg-gray-50 dark:hover:bg-gray-700`) → `nav-item-hover`.
- `components/CardItem.js`: bulk-select checkbox
`focus:ring-purple-500` → ember.
### 4. `dark:` modifier classes (broken with `[data-theme]` theming)
This app uses `[data-theme="dark"]` CSS selector theming, not
Tailwind's `class` strategy, so `dark:bg-green-900/20` etc. produced
no CSS in dark mode. Affected alerts on `pages/settings.js` and
`pages/profile.js` — replaced with `glass-panel` + semantic border
colour (flame for success, #dc2626 for error).
`pages/settings.js` sidebar nav also moved off its hardcoded full-ember
fill onto the system `nav-item` / `nav-item-active` / `nav-item-hover`
pattern for consistency with the global sidebar.
## Verification
- `npm run build` — green (Next 16 + Turbopack)
- `npm run lint` — 0 errors, 1 unrelated pre-existing warning
- `npm run test:run` — 113/113 pass (no test changes needed)
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-06-04 15:06:22 -04:00
|
|
|
<div
|
|
|
|
|
className="w-8 h-8 rounded-full flex items-center justify-center mr-3"
|
|
|
|
|
style={{
|
|
|
|
|
background:
|
|
|
|
|
'linear-gradient(135deg, var(--accent-ember) 0%, var(--accent-flame) 100%)',
|
|
|
|
|
}}
|
|
|
|
|
>
|
2025-07-25 23:28:52 -04:00
|
|
|
<span className="text-white text-sm font-bold">
|
|
|
|
|
{user.email.charAt(0).toUpperCase()}
|
|
|
|
|
</span>
|
|
|
|
|
</div>
|
|
|
|
|
<div>
|
refactor(design): site-wide sweep — broken Tailwind tokens, rounded corners, SearchBar primitive (#117) (#117)
Comprehensive design sweep across the rest of the app following the
shipped Liquid Glass + corner-border-light system (#116).
## Three classes of finding
### 1. Broken Tailwind token classes (HIGH — pages were unstyled)
The decks / deck-builder / deck-detail cluster relied on Tailwind
classes that don't exist in `tailwind.config.js` (no `bg-bg-*`,
`text-text-*`, `border-border`, `bg-accent-ember`,
`focus:ring-accent-ember`, `hover:bg-accent-ember-dark`). Those classes
produced ZERO CSS — backgrounds were transparent, borders invisible,
hover states absent.
Rewrote with inline `style={{ ... CSS vars ... }}` + the `<Button>` /
`<SearchBar>` primitives + `glass-panel` surfaces:
- `pages/decks.js` (full page)
- `pages/deck/[id].js` (header, stats sidebar, group-by controls,
card list)
- `pages/deck-builder.js` (loading spinner)
- `components/DeckBuilderView.js` (toolbar + main panel)
- `components/DeckBuilderCardBrowser.js` (full rewrite; integrated
`<SearchBar>` for the card-picker input)
- `components/DeckBuilderDeckList.js` (full rewrite)
- `components/DeckBuilderStatsBar.js`
- `components/ManaSymbolSettings.js`
- `components/ManaSymbols.js` (single `text-text-secondary`)
- `pages/admin/card-editor.js` cluster was already clean
### 2. Duplicative / stale page searches
Replaced raw `<input>` search controls with the `<SearchBar>` primitive
(adds clear button, ember focus ring, system-consistent rounded
corners). Kept page-specific filter searches (they filter the visible
list — distinct from the global TopSearchBar command palette):
- `pages/my-cards.js`
- `pages/community/collections.js`
- `components/CardsPageView.js`
- `components/CollectionPageView.js`
- `components/DeckBuilderCardBrowser.js`
`pages/my-cards.js` filter wrapper also lifted into a `glass-panel`
chip instead of a solid `var(--bg-primary)` band.
### 3. Square corners + stale palette in shared views
- `components/CollectionPageView.js`: 10 action buttons (`rounded-lg`
+ `hover:bg-gray-50`) → `rounded-xl` + `nav-item-hover`; 4 filter
selects (`focus:ring-purple-500 rounded-lg`) → `.input-field`;
view-mode toggle (`bg-white text-gray-900` — invisible in dark mode)
→ tokenised; SYSTEM badge gradient (`from-blue-500 to-purple-600`)
→ ember↔flame; tooltip (`bg-gray-900`) → `glass-panel-strong`;
search-results dropdown (`bg-white border-gray-200` — invisible in
dark mode) → `glass-panel-strong`; Activity / game-count /
TCG-game badges palette-aligned.
- `components/CardsPageView.js`: "Load More Cards" button
(`bg-gradient-to-r from-blue-500 to-purple-600 rounded-lg`) →
`<Button variant="primary" size="lg">`.
- `components/CollectionsPageView.js`: matching SYSTEM badge +
tooltip cleanup.
- `components/ShareModal.js`: user-search dropdown
(`border-gray-200 hover:bg-gray-50`) and email-invite card moved
onto `glass-panel` + `nav-item-hover`; social-share buttons
`rounded-lg hover:bg-gray-50` → `rounded-xl nav-item-hover`.
- `components/Layout.js`: profile-menu dropdown row
(`hover:bg-gray-50 dark:hover:bg-gray-700`) → `nav-item-hover`.
- `components/CardItem.js`: bulk-select checkbox
`focus:ring-purple-500` → ember.
### 4. `dark:` modifier classes (broken with `[data-theme]` theming)
This app uses `[data-theme="dark"]` CSS selector theming, not
Tailwind's `class` strategy, so `dark:bg-green-900/20` etc. produced
no CSS in dark mode. Affected alerts on `pages/settings.js` and
`pages/profile.js` — replaced with `glass-panel` + semantic border
colour (flame for success, #dc2626 for error).
`pages/settings.js` sidebar nav also moved off its hardcoded full-ember
fill onto the system `nav-item` / `nav-item-active` / `nav-item-hover`
pattern for consistency with the global sidebar.
## Verification
- `npm run build` — green (Next 16 + Turbopack)
- `npm run lint` — 0 errors, 1 unrelated pre-existing warning
- `npm run test:run` — 113/113 pass (no test changes needed)
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-06-04 15:06:22 -04:00
|
|
|
<div className="font-medium" style={{ color: 'var(--text-primary)' }}>
|
|
|
|
|
{user.email}
|
|
|
|
|
</div>
|
|
|
|
|
<div className="text-sm" style={{ color: 'var(--text-secondary)' }}>
|
|
|
|
|
Click to invite as viewer
|
|
|
|
|
</div>
|
2025-07-25 23:28:52 -04:00
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
))}
|
|
|
|
|
</div>
|
|
|
|
|
)}
|
|
|
|
|
|
|
|
|
|
{/* Email invite option */}
|
|
|
|
|
{searchQuery && isValidEmail(searchQuery) && !searchResults.some(u => u.email === searchQuery) && (
|
refactor(design): site-wide sweep — broken Tailwind tokens, rounded corners, SearchBar primitive (#117) (#117)
Comprehensive design sweep across the rest of the app following the
shipped Liquid Glass + corner-border-light system (#116).
## Three classes of finding
### 1. Broken Tailwind token classes (HIGH — pages were unstyled)
The decks / deck-builder / deck-detail cluster relied on Tailwind
classes that don't exist in `tailwind.config.js` (no `bg-bg-*`,
`text-text-*`, `border-border`, `bg-accent-ember`,
`focus:ring-accent-ember`, `hover:bg-accent-ember-dark`). Those classes
produced ZERO CSS — backgrounds were transparent, borders invisible,
hover states absent.
Rewrote with inline `style={{ ... CSS vars ... }}` + the `<Button>` /
`<SearchBar>` primitives + `glass-panel` surfaces:
- `pages/decks.js` (full page)
- `pages/deck/[id].js` (header, stats sidebar, group-by controls,
card list)
- `pages/deck-builder.js` (loading spinner)
- `components/DeckBuilderView.js` (toolbar + main panel)
- `components/DeckBuilderCardBrowser.js` (full rewrite; integrated
`<SearchBar>` for the card-picker input)
- `components/DeckBuilderDeckList.js` (full rewrite)
- `components/DeckBuilderStatsBar.js`
- `components/ManaSymbolSettings.js`
- `components/ManaSymbols.js` (single `text-text-secondary`)
- `pages/admin/card-editor.js` cluster was already clean
### 2. Duplicative / stale page searches
Replaced raw `<input>` search controls with the `<SearchBar>` primitive
(adds clear button, ember focus ring, system-consistent rounded
corners). Kept page-specific filter searches (they filter the visible
list — distinct from the global TopSearchBar command palette):
- `pages/my-cards.js`
- `pages/community/collections.js`
- `components/CardsPageView.js`
- `components/CollectionPageView.js`
- `components/DeckBuilderCardBrowser.js`
`pages/my-cards.js` filter wrapper also lifted into a `glass-panel`
chip instead of a solid `var(--bg-primary)` band.
### 3. Square corners + stale palette in shared views
- `components/CollectionPageView.js`: 10 action buttons (`rounded-lg`
+ `hover:bg-gray-50`) → `rounded-xl` + `nav-item-hover`; 4 filter
selects (`focus:ring-purple-500 rounded-lg`) → `.input-field`;
view-mode toggle (`bg-white text-gray-900` — invisible in dark mode)
→ tokenised; SYSTEM badge gradient (`from-blue-500 to-purple-600`)
→ ember↔flame; tooltip (`bg-gray-900`) → `glass-panel-strong`;
search-results dropdown (`bg-white border-gray-200` — invisible in
dark mode) → `glass-panel-strong`; Activity / game-count /
TCG-game badges palette-aligned.
- `components/CardsPageView.js`: "Load More Cards" button
(`bg-gradient-to-r from-blue-500 to-purple-600 rounded-lg`) →
`<Button variant="primary" size="lg">`.
- `components/CollectionsPageView.js`: matching SYSTEM badge +
tooltip cleanup.
- `components/ShareModal.js`: user-search dropdown
(`border-gray-200 hover:bg-gray-50`) and email-invite card moved
onto `glass-panel` + `nav-item-hover`; social-share buttons
`rounded-lg hover:bg-gray-50` → `rounded-xl nav-item-hover`.
- `components/Layout.js`: profile-menu dropdown row
(`hover:bg-gray-50 dark:hover:bg-gray-700`) → `nav-item-hover`.
- `components/CardItem.js`: bulk-select checkbox
`focus:ring-purple-500` → ember.
### 4. `dark:` modifier classes (broken with `[data-theme]` theming)
This app uses `[data-theme="dark"]` CSS selector theming, not
Tailwind's `class` strategy, so `dark:bg-green-900/20` etc. produced
no CSS in dark mode. Affected alerts on `pages/settings.js` and
`pages/profile.js` — replaced with `glass-panel` + semantic border
colour (flame for success, #dc2626 for error).
`pages/settings.js` sidebar nav also moved off its hardcoded full-ember
fill onto the system `nav-item` / `nav-item-active` / `nav-item-hover`
pattern for consistency with the global sidebar.
## Verification
- `npm run build` — green (Next 16 + Turbopack)
- `npm run lint` — 0 errors, 1 unrelated pre-existing warning
- `npm run test:run` — 113/113 pass (no test changes needed)
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-06-04 15:06:22 -04:00
|
|
|
<div className="mt-2 rounded-xl glass-panel">
|
2025-07-25 23:28:52 -04:00
|
|
|
<div
|
|
|
|
|
onClick={() => handleInvite(searchQuery)}
|
refactor(design): site-wide sweep — broken Tailwind tokens, rounded corners, SearchBar primitive (#117) (#117)
Comprehensive design sweep across the rest of the app following the
shipped Liquid Glass + corner-border-light system (#116).
## Three classes of finding
### 1. Broken Tailwind token classes (HIGH — pages were unstyled)
The decks / deck-builder / deck-detail cluster relied on Tailwind
classes that don't exist in `tailwind.config.js` (no `bg-bg-*`,
`text-text-*`, `border-border`, `bg-accent-ember`,
`focus:ring-accent-ember`, `hover:bg-accent-ember-dark`). Those classes
produced ZERO CSS — backgrounds were transparent, borders invisible,
hover states absent.
Rewrote with inline `style={{ ... CSS vars ... }}` + the `<Button>` /
`<SearchBar>` primitives + `glass-panel` surfaces:
- `pages/decks.js` (full page)
- `pages/deck/[id].js` (header, stats sidebar, group-by controls,
card list)
- `pages/deck-builder.js` (loading spinner)
- `components/DeckBuilderView.js` (toolbar + main panel)
- `components/DeckBuilderCardBrowser.js` (full rewrite; integrated
`<SearchBar>` for the card-picker input)
- `components/DeckBuilderDeckList.js` (full rewrite)
- `components/DeckBuilderStatsBar.js`
- `components/ManaSymbolSettings.js`
- `components/ManaSymbols.js` (single `text-text-secondary`)
- `pages/admin/card-editor.js` cluster was already clean
### 2. Duplicative / stale page searches
Replaced raw `<input>` search controls with the `<SearchBar>` primitive
(adds clear button, ember focus ring, system-consistent rounded
corners). Kept page-specific filter searches (they filter the visible
list — distinct from the global TopSearchBar command palette):
- `pages/my-cards.js`
- `pages/community/collections.js`
- `components/CardsPageView.js`
- `components/CollectionPageView.js`
- `components/DeckBuilderCardBrowser.js`
`pages/my-cards.js` filter wrapper also lifted into a `glass-panel`
chip instead of a solid `var(--bg-primary)` band.
### 3. Square corners + stale palette in shared views
- `components/CollectionPageView.js`: 10 action buttons (`rounded-lg`
+ `hover:bg-gray-50`) → `rounded-xl` + `nav-item-hover`; 4 filter
selects (`focus:ring-purple-500 rounded-lg`) → `.input-field`;
view-mode toggle (`bg-white text-gray-900` — invisible in dark mode)
→ tokenised; SYSTEM badge gradient (`from-blue-500 to-purple-600`)
→ ember↔flame; tooltip (`bg-gray-900`) → `glass-panel-strong`;
search-results dropdown (`bg-white border-gray-200` — invisible in
dark mode) → `glass-panel-strong`; Activity / game-count /
TCG-game badges palette-aligned.
- `components/CardsPageView.js`: "Load More Cards" button
(`bg-gradient-to-r from-blue-500 to-purple-600 rounded-lg`) →
`<Button variant="primary" size="lg">`.
- `components/CollectionsPageView.js`: matching SYSTEM badge +
tooltip cleanup.
- `components/ShareModal.js`: user-search dropdown
(`border-gray-200 hover:bg-gray-50`) and email-invite card moved
onto `glass-panel` + `nav-item-hover`; social-share buttons
`rounded-lg hover:bg-gray-50` → `rounded-xl nav-item-hover`.
- `components/Layout.js`: profile-menu dropdown row
(`hover:bg-gray-50 dark:hover:bg-gray-700`) → `nav-item-hover`.
- `components/CardItem.js`: bulk-select checkbox
`focus:ring-purple-500` → ember.
### 4. `dark:` modifier classes (broken with `[data-theme]` theming)
This app uses `[data-theme="dark"]` CSS selector theming, not
Tailwind's `class` strategy, so `dark:bg-green-900/20` etc. produced
no CSS in dark mode. Affected alerts on `pages/settings.js` and
`pages/profile.js` — replaced with `glass-panel` + semantic border
colour (flame for success, #dc2626 for error).
`pages/settings.js` sidebar nav also moved off its hardcoded full-ember
fill onto the system `nav-item` / `nav-item-active` / `nav-item-hover`
pattern for consistency with the global sidebar.
## Verification
- `npm run build` — green (Next 16 + Turbopack)
- `npm run lint` — 0 errors, 1 unrelated pre-existing warning
- `npm run test:run` — 113/113 pass (no test changes needed)
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-06-04 15:06:22 -04:00
|
|
|
className="flex items-center p-3 cursor-pointer nav-item-hover"
|
2025-07-25 23:28:52 -04:00
|
|
|
>
|
refactor(design): site-wide sweep — broken Tailwind tokens, rounded corners, SearchBar primitive (#117) (#117)
Comprehensive design sweep across the rest of the app following the
shipped Liquid Glass + corner-border-light system (#116).
## Three classes of finding
### 1. Broken Tailwind token classes (HIGH — pages were unstyled)
The decks / deck-builder / deck-detail cluster relied on Tailwind
classes that don't exist in `tailwind.config.js` (no `bg-bg-*`,
`text-text-*`, `border-border`, `bg-accent-ember`,
`focus:ring-accent-ember`, `hover:bg-accent-ember-dark`). Those classes
produced ZERO CSS — backgrounds were transparent, borders invisible,
hover states absent.
Rewrote with inline `style={{ ... CSS vars ... }}` + the `<Button>` /
`<SearchBar>` primitives + `glass-panel` surfaces:
- `pages/decks.js` (full page)
- `pages/deck/[id].js` (header, stats sidebar, group-by controls,
card list)
- `pages/deck-builder.js` (loading spinner)
- `components/DeckBuilderView.js` (toolbar + main panel)
- `components/DeckBuilderCardBrowser.js` (full rewrite; integrated
`<SearchBar>` for the card-picker input)
- `components/DeckBuilderDeckList.js` (full rewrite)
- `components/DeckBuilderStatsBar.js`
- `components/ManaSymbolSettings.js`
- `components/ManaSymbols.js` (single `text-text-secondary`)
- `pages/admin/card-editor.js` cluster was already clean
### 2. Duplicative / stale page searches
Replaced raw `<input>` search controls with the `<SearchBar>` primitive
(adds clear button, ember focus ring, system-consistent rounded
corners). Kept page-specific filter searches (they filter the visible
list — distinct from the global TopSearchBar command palette):
- `pages/my-cards.js`
- `pages/community/collections.js`
- `components/CardsPageView.js`
- `components/CollectionPageView.js`
- `components/DeckBuilderCardBrowser.js`
`pages/my-cards.js` filter wrapper also lifted into a `glass-panel`
chip instead of a solid `var(--bg-primary)` band.
### 3. Square corners + stale palette in shared views
- `components/CollectionPageView.js`: 10 action buttons (`rounded-lg`
+ `hover:bg-gray-50`) → `rounded-xl` + `nav-item-hover`; 4 filter
selects (`focus:ring-purple-500 rounded-lg`) → `.input-field`;
view-mode toggle (`bg-white text-gray-900` — invisible in dark mode)
→ tokenised; SYSTEM badge gradient (`from-blue-500 to-purple-600`)
→ ember↔flame; tooltip (`bg-gray-900`) → `glass-panel-strong`;
search-results dropdown (`bg-white border-gray-200` — invisible in
dark mode) → `glass-panel-strong`; Activity / game-count /
TCG-game badges palette-aligned.
- `components/CardsPageView.js`: "Load More Cards" button
(`bg-gradient-to-r from-blue-500 to-purple-600 rounded-lg`) →
`<Button variant="primary" size="lg">`.
- `components/CollectionsPageView.js`: matching SYSTEM badge +
tooltip cleanup.
- `components/ShareModal.js`: user-search dropdown
(`border-gray-200 hover:bg-gray-50`) and email-invite card moved
onto `glass-panel` + `nav-item-hover`; social-share buttons
`rounded-lg hover:bg-gray-50` → `rounded-xl nav-item-hover`.
- `components/Layout.js`: profile-menu dropdown row
(`hover:bg-gray-50 dark:hover:bg-gray-700`) → `nav-item-hover`.
- `components/CardItem.js`: bulk-select checkbox
`focus:ring-purple-500` → ember.
### 4. `dark:` modifier classes (broken with `[data-theme]` theming)
This app uses `[data-theme="dark"]` CSS selector theming, not
Tailwind's `class` strategy, so `dark:bg-green-900/20` etc. produced
no CSS in dark mode. Affected alerts on `pages/settings.js` and
`pages/profile.js` — replaced with `glass-panel` + semantic border
colour (flame for success, #dc2626 for error).
`pages/settings.js` sidebar nav also moved off its hardcoded full-ember
fill onto the system `nav-item` / `nav-item-active` / `nav-item-hover`
pattern for consistency with the global sidebar.
## Verification
- `npm run build` — green (Next 16 + Turbopack)
- `npm run lint` — 0 errors, 1 unrelated pre-existing warning
- `npm run test:run` — 113/113 pass (no test changes needed)
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-06-04 15:06:22 -04:00
|
|
|
<div
|
|
|
|
|
className="w-8 h-8 rounded-full flex items-center justify-center mr-3"
|
|
|
|
|
style={{ backgroundColor: 'var(--bg-tertiary)' }}
|
|
|
|
|
>
|
|
|
|
|
<svg
|
|
|
|
|
className="w-4 h-4"
|
|
|
|
|
style={{ color: 'var(--text-primary)' }}
|
|
|
|
|
fill="none"
|
|
|
|
|
stroke="currentColor"
|
|
|
|
|
viewBox="0 0 24 24"
|
|
|
|
|
>
|
2025-07-25 23:28:52 -04:00
|
|
|
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M16 12a4 4 0 10-8 0 4 4 0 008 0zm0 0v1.5a2.5 2.5 0 005 0V12a9 9 0 10-9 9m4.5-1.206a8.959 8.959 0 01-4.5 1.207" />
|
|
|
|
|
</svg>
|
|
|
|
|
</div>
|
|
|
|
|
<div>
|
refactor(design): site-wide sweep — broken Tailwind tokens, rounded corners, SearchBar primitive (#117) (#117)
Comprehensive design sweep across the rest of the app following the
shipped Liquid Glass + corner-border-light system (#116).
## Three classes of finding
### 1. Broken Tailwind token classes (HIGH — pages were unstyled)
The decks / deck-builder / deck-detail cluster relied on Tailwind
classes that don't exist in `tailwind.config.js` (no `bg-bg-*`,
`text-text-*`, `border-border`, `bg-accent-ember`,
`focus:ring-accent-ember`, `hover:bg-accent-ember-dark`). Those classes
produced ZERO CSS — backgrounds were transparent, borders invisible,
hover states absent.
Rewrote with inline `style={{ ... CSS vars ... }}` + the `<Button>` /
`<SearchBar>` primitives + `glass-panel` surfaces:
- `pages/decks.js` (full page)
- `pages/deck/[id].js` (header, stats sidebar, group-by controls,
card list)
- `pages/deck-builder.js` (loading spinner)
- `components/DeckBuilderView.js` (toolbar + main panel)
- `components/DeckBuilderCardBrowser.js` (full rewrite; integrated
`<SearchBar>` for the card-picker input)
- `components/DeckBuilderDeckList.js` (full rewrite)
- `components/DeckBuilderStatsBar.js`
- `components/ManaSymbolSettings.js`
- `components/ManaSymbols.js` (single `text-text-secondary`)
- `pages/admin/card-editor.js` cluster was already clean
### 2. Duplicative / stale page searches
Replaced raw `<input>` search controls with the `<SearchBar>` primitive
(adds clear button, ember focus ring, system-consistent rounded
corners). Kept page-specific filter searches (they filter the visible
list — distinct from the global TopSearchBar command palette):
- `pages/my-cards.js`
- `pages/community/collections.js`
- `components/CardsPageView.js`
- `components/CollectionPageView.js`
- `components/DeckBuilderCardBrowser.js`
`pages/my-cards.js` filter wrapper also lifted into a `glass-panel`
chip instead of a solid `var(--bg-primary)` band.
### 3. Square corners + stale palette in shared views
- `components/CollectionPageView.js`: 10 action buttons (`rounded-lg`
+ `hover:bg-gray-50`) → `rounded-xl` + `nav-item-hover`; 4 filter
selects (`focus:ring-purple-500 rounded-lg`) → `.input-field`;
view-mode toggle (`bg-white text-gray-900` — invisible in dark mode)
→ tokenised; SYSTEM badge gradient (`from-blue-500 to-purple-600`)
→ ember↔flame; tooltip (`bg-gray-900`) → `glass-panel-strong`;
search-results dropdown (`bg-white border-gray-200` — invisible in
dark mode) → `glass-panel-strong`; Activity / game-count /
TCG-game badges palette-aligned.
- `components/CardsPageView.js`: "Load More Cards" button
(`bg-gradient-to-r from-blue-500 to-purple-600 rounded-lg`) →
`<Button variant="primary" size="lg">`.
- `components/CollectionsPageView.js`: matching SYSTEM badge +
tooltip cleanup.
- `components/ShareModal.js`: user-search dropdown
(`border-gray-200 hover:bg-gray-50`) and email-invite card moved
onto `glass-panel` + `nav-item-hover`; social-share buttons
`rounded-lg hover:bg-gray-50` → `rounded-xl nav-item-hover`.
- `components/Layout.js`: profile-menu dropdown row
(`hover:bg-gray-50 dark:hover:bg-gray-700`) → `nav-item-hover`.
- `components/CardItem.js`: bulk-select checkbox
`focus:ring-purple-500` → ember.
### 4. `dark:` modifier classes (broken with `[data-theme]` theming)
This app uses `[data-theme="dark"]` CSS selector theming, not
Tailwind's `class` strategy, so `dark:bg-green-900/20` etc. produced
no CSS in dark mode. Affected alerts on `pages/settings.js` and
`pages/profile.js` — replaced with `glass-panel` + semantic border
colour (flame for success, #dc2626 for error).
`pages/settings.js` sidebar nav also moved off its hardcoded full-ember
fill onto the system `nav-item` / `nav-item-active` / `nav-item-hover`
pattern for consistency with the global sidebar.
## Verification
- `npm run build` — green (Next 16 + Turbopack)
- `npm run lint` — 0 errors, 1 unrelated pre-existing warning
- `npm run test:run` — 113/113 pass (no test changes needed)
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-06-04 15:06:22 -04:00
|
|
|
<div className="font-medium" style={{ color: 'var(--text-primary)' }}>
|
|
|
|
|
Invite {searchQuery}
|
|
|
|
|
</div>
|
|
|
|
|
<div className="text-sm" style={{ color: 'var(--text-secondary)' }}>
|
|
|
|
|
Send email invitation as viewer
|
|
|
|
|
</div>
|
2025-07-25 23:28:52 -04:00
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
)}
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
{/* Current Permissions */}
|
|
|
|
|
<div className="mb-6">
|
2026-06-04 17:55:39 -04:00
|
|
|
<p
|
|
|
|
|
className="text-sm mb-3"
|
|
|
|
|
style={{ color: 'var(--text-secondary)' }}
|
|
|
|
|
>
|
2026-05-29 10:53:40 -04:00
|
|
|
Only those invited can view or collaborate on this list.
|
2025-07-25 23:28:52 -04:00
|
|
|
</p>
|
2026-06-04 17:55:39 -04:00
|
|
|
|
2025-07-25 23:28:52 -04:00
|
|
|
<div className="space-y-2">
|
|
|
|
|
{/* Current User */}
|
|
|
|
|
{currentUser && (
|
2026-06-04 17:55:39 -04:00
|
|
|
<div
|
|
|
|
|
className="flex items-center justify-between p-3 rounded-xl"
|
|
|
|
|
style={{
|
|
|
|
|
backgroundColor: 'var(--bg-secondary)',
|
|
|
|
|
border: '1px solid var(--border)',
|
|
|
|
|
}}
|
|
|
|
|
>
|
2025-07-25 23:28:52 -04:00
|
|
|
<div className="flex items-center">
|
2026-06-04 17:55:39 -04:00
|
|
|
<div
|
|
|
|
|
className="w-8 h-8 rounded-full flex items-center justify-center mr-3"
|
|
|
|
|
style={{
|
|
|
|
|
background:
|
|
|
|
|
'linear-gradient(135deg, var(--accent-ember) 0%, var(--accent-flame) 100%)',
|
|
|
|
|
}}
|
|
|
|
|
>
|
2025-07-25 23:28:52 -04:00
|
|
|
<span className="text-white text-sm font-bold">
|
|
|
|
|
{currentUser.email.charAt(0).toUpperCase()}
|
|
|
|
|
</span>
|
|
|
|
|
</div>
|
|
|
|
|
<div>
|
2026-06-04 17:55:39 -04:00
|
|
|
<div
|
|
|
|
|
className="font-medium"
|
|
|
|
|
style={{ color: 'var(--text-primary)' }}
|
|
|
|
|
>
|
2025-07-25 23:28:52 -04:00
|
|
|
{currentUser.email} (You)
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
2026-06-04 17:55:39 -04:00
|
|
|
<span
|
|
|
|
|
className="text-sm px-2 py-1 rounded-xl"
|
|
|
|
|
style={{
|
|
|
|
|
backgroundColor: 'var(--bg-tertiary)',
|
|
|
|
|
color: 'var(--text-secondary)',
|
|
|
|
|
border: '1px solid var(--border)',
|
|
|
|
|
}}
|
|
|
|
|
>
|
2025-07-25 23:28:52 -04:00
|
|
|
Owner
|
|
|
|
|
</span>
|
|
|
|
|
</div>
|
|
|
|
|
)}
|
|
|
|
|
|
|
|
|
|
{/* Invited Users */}
|
|
|
|
|
{invitedUsers.map((permission) => (
|
2026-06-04 17:55:39 -04:00
|
|
|
<div
|
|
|
|
|
key={permission.id}
|
|
|
|
|
className="flex items-center justify-between p-3 rounded-xl"
|
|
|
|
|
style={{
|
|
|
|
|
border: '1px solid var(--border)',
|
|
|
|
|
backgroundColor: 'var(--bg-secondary)',
|
|
|
|
|
}}
|
|
|
|
|
>
|
2025-07-25 23:28:52 -04:00
|
|
|
<div className="flex items-center">
|
2026-06-04 17:55:39 -04:00
|
|
|
<div
|
|
|
|
|
className="w-8 h-8 rounded-full flex items-center justify-center mr-3"
|
|
|
|
|
style={{
|
|
|
|
|
backgroundColor: 'var(--bg-tertiary)',
|
|
|
|
|
border: '1px solid var(--border)',
|
|
|
|
|
}}
|
|
|
|
|
>
|
|
|
|
|
<span
|
|
|
|
|
className="text-sm font-bold"
|
|
|
|
|
style={{ color: 'var(--text-primary)' }}
|
|
|
|
|
>
|
2025-07-25 23:28:52 -04:00
|
|
|
{permission.user_email?.charAt(0).toUpperCase() || '?'}
|
|
|
|
|
</span>
|
|
|
|
|
</div>
|
|
|
|
|
<div>
|
2026-06-04 17:55:39 -04:00
|
|
|
<div
|
|
|
|
|
className="font-medium"
|
|
|
|
|
style={{ color: 'var(--text-primary)' }}
|
|
|
|
|
>
|
2025-07-25 23:28:52 -04:00
|
|
|
{permission.user_email || 'Unknown User'}
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
2026-06-04 17:55:39 -04:00
|
|
|
<span
|
|
|
|
|
className="text-sm px-2 py-1 rounded-xl"
|
|
|
|
|
style={{
|
|
|
|
|
backgroundColor: 'var(--bg-tertiary)',
|
|
|
|
|
color: 'var(--text-secondary)',
|
|
|
|
|
}}
|
|
|
|
|
>
|
2025-07-25 23:28:52 -04:00
|
|
|
{permission.role === 'editor' ? 'Collaborator' : 'Viewer'}
|
|
|
|
|
</span>
|
|
|
|
|
</div>
|
|
|
|
|
))}
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
{/* Share Link */}
|
|
|
|
|
<div className="mb-6">
|
|
|
|
|
<div className="flex space-x-2">
|
|
|
|
|
<input
|
|
|
|
|
type="text"
|
|
|
|
|
value={window.location.href}
|
|
|
|
|
readOnly
|
2026-06-04 17:55:39 -04:00
|
|
|
className="flex-1 px-3 py-2 rounded-xl text-sm"
|
|
|
|
|
style={{
|
|
|
|
|
backgroundColor: 'var(--input-bg)',
|
|
|
|
|
border: '1px solid var(--input-border)',
|
|
|
|
|
color: 'var(--text-primary)',
|
|
|
|
|
}}
|
2025-07-25 23:28:52 -04:00
|
|
|
/>
|
2026-06-04 17:55:39 -04:00
|
|
|
{copySuccess ? (
|
|
|
|
|
<span
|
|
|
|
|
className="px-4 py-2 rounded-xl text-sm font-medium inline-flex items-center"
|
|
|
|
|
style={{
|
|
|
|
|
backgroundColor: 'var(--bg-secondary)',
|
|
|
|
|
color: 'var(--accent-flame)',
|
|
|
|
|
border: '1px solid var(--accent-ember)',
|
|
|
|
|
}}
|
|
|
|
|
>
|
|
|
|
|
Copied!
|
|
|
|
|
</span>
|
|
|
|
|
) : (
|
|
|
|
|
<Button variant="primary" onClick={handleCopyLink}>
|
|
|
|
|
Copy link
|
|
|
|
|
</Button>
|
|
|
|
|
)}
|
2025-07-25 23:28:52 -04:00
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
{/* Social Share */}
|
|
|
|
|
<div className="grid grid-cols-4 gap-4">
|
|
|
|
|
{[
|
|
|
|
|
{ name: 'Twitter', icon: 'twitter', platform: 'twitter' },
|
|
|
|
|
{ name: 'Facebook', icon: 'facebook', platform: 'facebook' },
|
|
|
|
|
{ name: 'Reddit', icon: 'reddit', platform: 'reddit' },
|
|
|
|
|
{ name: 'Discord', icon: 'discord', platform: 'discord' }
|
|
|
|
|
].map((social) => (
|
|
|
|
|
<button
|
|
|
|
|
key={social.platform}
|
|
|
|
|
onClick={() => handleSocialShare(social.platform)}
|
refactor(design): site-wide sweep — broken Tailwind tokens, rounded corners, SearchBar primitive (#117) (#117)
Comprehensive design sweep across the rest of the app following the
shipped Liquid Glass + corner-border-light system (#116).
## Three classes of finding
### 1. Broken Tailwind token classes (HIGH — pages were unstyled)
The decks / deck-builder / deck-detail cluster relied on Tailwind
classes that don't exist in `tailwind.config.js` (no `bg-bg-*`,
`text-text-*`, `border-border`, `bg-accent-ember`,
`focus:ring-accent-ember`, `hover:bg-accent-ember-dark`). Those classes
produced ZERO CSS — backgrounds were transparent, borders invisible,
hover states absent.
Rewrote with inline `style={{ ... CSS vars ... }}` + the `<Button>` /
`<SearchBar>` primitives + `glass-panel` surfaces:
- `pages/decks.js` (full page)
- `pages/deck/[id].js` (header, stats sidebar, group-by controls,
card list)
- `pages/deck-builder.js` (loading spinner)
- `components/DeckBuilderView.js` (toolbar + main panel)
- `components/DeckBuilderCardBrowser.js` (full rewrite; integrated
`<SearchBar>` for the card-picker input)
- `components/DeckBuilderDeckList.js` (full rewrite)
- `components/DeckBuilderStatsBar.js`
- `components/ManaSymbolSettings.js`
- `components/ManaSymbols.js` (single `text-text-secondary`)
- `pages/admin/card-editor.js` cluster was already clean
### 2. Duplicative / stale page searches
Replaced raw `<input>` search controls with the `<SearchBar>` primitive
(adds clear button, ember focus ring, system-consistent rounded
corners). Kept page-specific filter searches (they filter the visible
list — distinct from the global TopSearchBar command palette):
- `pages/my-cards.js`
- `pages/community/collections.js`
- `components/CardsPageView.js`
- `components/CollectionPageView.js`
- `components/DeckBuilderCardBrowser.js`
`pages/my-cards.js` filter wrapper also lifted into a `glass-panel`
chip instead of a solid `var(--bg-primary)` band.
### 3. Square corners + stale palette in shared views
- `components/CollectionPageView.js`: 10 action buttons (`rounded-lg`
+ `hover:bg-gray-50`) → `rounded-xl` + `nav-item-hover`; 4 filter
selects (`focus:ring-purple-500 rounded-lg`) → `.input-field`;
view-mode toggle (`bg-white text-gray-900` — invisible in dark mode)
→ tokenised; SYSTEM badge gradient (`from-blue-500 to-purple-600`)
→ ember↔flame; tooltip (`bg-gray-900`) → `glass-panel-strong`;
search-results dropdown (`bg-white border-gray-200` — invisible in
dark mode) → `glass-panel-strong`; Activity / game-count /
TCG-game badges palette-aligned.
- `components/CardsPageView.js`: "Load More Cards" button
(`bg-gradient-to-r from-blue-500 to-purple-600 rounded-lg`) →
`<Button variant="primary" size="lg">`.
- `components/CollectionsPageView.js`: matching SYSTEM badge +
tooltip cleanup.
- `components/ShareModal.js`: user-search dropdown
(`border-gray-200 hover:bg-gray-50`) and email-invite card moved
onto `glass-panel` + `nav-item-hover`; social-share buttons
`rounded-lg hover:bg-gray-50` → `rounded-xl nav-item-hover`.
- `components/Layout.js`: profile-menu dropdown row
(`hover:bg-gray-50 dark:hover:bg-gray-700`) → `nav-item-hover`.
- `components/CardItem.js`: bulk-select checkbox
`focus:ring-purple-500` → ember.
### 4. `dark:` modifier classes (broken with `[data-theme]` theming)
This app uses `[data-theme="dark"]` CSS selector theming, not
Tailwind's `class` strategy, so `dark:bg-green-900/20` etc. produced
no CSS in dark mode. Affected alerts on `pages/settings.js` and
`pages/profile.js` — replaced with `glass-panel` + semantic border
colour (flame for success, #dc2626 for error).
`pages/settings.js` sidebar nav also moved off its hardcoded full-ember
fill onto the system `nav-item` / `nav-item-active` / `nav-item-hover`
pattern for consistency with the global sidebar.
## Verification
- `npm run build` — green (Next 16 + Turbopack)
- `npm run lint` — 0 errors, 1 unrelated pre-existing warning
- `npm run test:run` — 113/113 pass (no test changes needed)
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-06-04 15:06:22 -04:00
|
|
|
className="flex flex-col items-center p-3 rounded-xl border nav-item-hover transition-colors"
|
|
|
|
|
style={{ borderColor: 'var(--border)' }}
|
2025-07-25 23:28:52 -04:00
|
|
|
>
|
2026-06-04 17:55:39 -04:00
|
|
|
<div
|
|
|
|
|
className="w-8 h-8 mb-2"
|
|
|
|
|
style={{ color: 'var(--text-secondary)' }}
|
|
|
|
|
>
|
2025-07-25 23:28:52 -04:00
|
|
|
{social.icon === 'twitter' && (
|
|
|
|
|
<svg viewBox="0 0 24 24" fill="currentColor">
|
|
|
|
|
<path d="M23.953 4.57a10 10 0 01-2.825.775 4.958 4.958 0 002.163-2.723c-.951.555-2.005.959-3.127 1.184a4.92 4.92 0 00-8.384 4.482C7.69 8.095 4.067 6.13 1.64 3.162a4.822 4.822 0 00-.666 2.475c0 1.71.87 3.213 2.188 4.096a4.904 4.904 0 01-2.228-.616v.06a4.923 4.923 0 003.946 4.827 4.996 4.996 0 01-2.212.085 4.936 4.936 0 004.604 3.417 9.867 9.867 0 01-6.102 2.105c-.39 0-.779-.023-1.17-.067a13.995 13.995 0 007.557 2.209c9.053 0 13.998-7.496 13.998-13.985 0-.21 0-.42-.015-.63A9.935 9.935 0 0024 4.59z"/>
|
|
|
|
|
</svg>
|
|
|
|
|
)}
|
|
|
|
|
{social.icon === 'facebook' && (
|
|
|
|
|
<svg viewBox="0 0 24 24" fill="currentColor">
|
|
|
|
|
<path d="M24 12.073c0-6.627-5.373-12-12-12s-12 5.373-12 12c0 5.99 4.388 10.954 10.125 11.854v-8.385H7.078v-3.47h3.047V9.43c0-3.007 1.792-4.669 4.533-4.669 1.312 0 2.686.235 2.686.235v2.953H15.83c-1.491 0-1.956.925-1.956 1.874v2.25h3.328l-.532 3.47h-2.796v8.385C19.612 23.027 24 18.062 24 12.073z"/>
|
|
|
|
|
</svg>
|
|
|
|
|
)}
|
|
|
|
|
{social.icon === 'reddit' && (
|
|
|
|
|
<svg viewBox="0 0 24 24" fill="currentColor">
|
|
|
|
|
<path d="M12 0A12 12 0 0 0 0 12a12 12 0 0 0 12 12 12 12 0 0 0 12-12A12 12 0 0 0 12 0zm5.01 4.744c.688 0 1.25.561 1.25 1.249a1.25 1.25 0 0 1-2.498.056l-2.597-.547-.8 3.747c1.824.07 3.48.632 4.674 1.488.308-.309.73-.491 1.207-.491.968 0 1.754.786 1.754 1.754 0 .716-.435 1.333-1.01 1.614a3.111 3.111 0 0 1 .042.52c0 2.694-3.13 4.87-7.004 4.87-3.874 0-7.004-2.176-7.004-4.87 0-.183.015-.366.043-.534A1.748 1.748 0 0 1 4.028 12c0-.968.786-1.754 1.754-1.754.463 0 .898.196 1.207.49 1.207-.883 2.878-1.43 4.744-1.487l.885-4.182a.342.342 0 0 1 .14-.197.35.35 0 0 1 .238-.042l2.906.617a1.214 1.214 0 0 1 1.108-.701zM9.25 12C8.561 12 8 12.562 8 13.25c0 .687.561 1.248 1.25 1.248.687 0 1.248-.561 1.248-1.249 0-.688-.561-1.249-1.249-1.249zm5.5 0c-.687 0-1.248.561-1.248 1.25 0 .687.561 1.248 1.249 1.248.688 0 1.249-.561 1.249-1.249 0-.687-.562-1.249-1.25-1.249zm-5.466 3.99a.327.327 0 0 0-.231.094.33.33 0 0 0 0 .463c.842.842 2.484.913 2.961.913.477 0 2.105-.056 2.961-.913a.361.361 0 0 0 .029-.463.33.33 0 0 0-.464 0c-.547.533-1.684.73-2.512.73-.828 0-1.979-.196-2.512-.73a.326.326 0 0 0-.232-.095z"/>
|
|
|
|
|
</svg>
|
|
|
|
|
)}
|
|
|
|
|
{social.icon === 'discord' && (
|
|
|
|
|
<svg viewBox="0 0 24 24" fill="currentColor">
|
|
|
|
|
<path d="M20.317 4.3698a19.7913 19.7913 0 00-4.8851-1.5152.0741.0741 0 00-.0785.0371c-.211.3753-.4447.8648-.6083 1.2495-1.8447-.2762-3.68-.2762-5.4868 0-.1636-.3933-.4058-.8742-.6177-1.2495a.077.077 0 00-.0785-.037 19.7363 19.7363 0 00-4.8852 1.515.0699.0699 0 00-.0321.0277C.5334 9.0458-.319 13.5799.0992 18.0578a.0824.0824 0 00.0312.0561c2.0528 1.5076 4.0413 2.4228 5.9929 3.0294a.0777.0777 0 00.0842-.0276c.4616-.6304.8731-1.2952 1.226-1.9942a.076.076 0 00-.0416-.1057c-.6528-.2476-1.2743-.5495-1.8722-.8923a.077.077 0 01-.0076-.1277c.1258-.0943.2517-.1923.3718-.2914a.0743.0743 0 01.0776-.0105c3.9278 1.7933 8.18 1.7933 12.0614 0a.0739.0739 0 01.0785.0095c.1202.099.246.1981.3728.2924a.077.077 0 01-.0066.1276 12.2986 12.2986 0 01-1.873.8914.0766.0766 0 00-.0407.1067c.3604.698.7719 1.3628 1.225 1.9932a.076.076 0 00.0842.0286c1.961-.6067 3.9495-1.5219 6.0023-3.0294a.077.077 0 00.0313-.0552c.5004-5.177-.8382-9.6739-3.5485-13.6604a.061.061 0 00-.0312-.0286zM8.02 15.3312c-1.1825 0-2.1569-1.0857-2.1569-2.419 0-1.3332.9555-2.4189 2.157-2.4189 1.2108 0 2.1757 1.0952 2.1568 2.419-.0002 1.3332-.9555 2.4189-2.1569 2.4189zm7.9748 0c-1.1825 0-2.1569-1.0857-2.1569-2.419 0-1.3332.9554-2.4189 2.1569-2.4189 1.2108 0 2.1757 1.0952 2.1568 2.419 0 1.3332-.9555 2.4189-2.1568 2.4189Z"/>
|
|
|
|
|
</svg>
|
|
|
|
|
)}
|
|
|
|
|
</div>
|
2026-06-04 17:55:39 -04:00
|
|
|
<span
|
|
|
|
|
className="text-xs"
|
|
|
|
|
style={{ color: 'var(--text-secondary)' }}
|
|
|
|
|
>
|
|
|
|
|
{social.name}
|
|
|
|
|
</span>
|
2025-07-25 23:28:52 -04:00
|
|
|
</button>
|
|
|
|
|
))}
|
|
|
|
|
</div>
|
feat(design-system): Liquid Glass redesign portfolio — foundation + primitives + Layout (#95)
* feat(design-system): Liquid Glass redesign portfolio — foundation + primitive kit + Layout shell
Operator-requested epic to migrate the UI from the current "warm panel + side-highlight + heavy gradient" visual language to a Liquid Glass aesthetic that retains Deck Hearth's fireplace warmth as accent / gradient / motion (not as panel fill). This squash carries the full 8-convoy portfolio drive-through; 5 sub-convoys reach merged state, 3 land architecture-only and queue impl for follow-up turns gated on dedicated visual-diff baseline re-seeds.
Sub-convoy #1 (liquid-glass-design-tokens) — MERGED. 29 CSS custom properties: glass-surface {low,mid,high} alpha ramp + blur/saturate + rim-light (inner/outer) + ember-rim (subtle/pronounced; RGB triple) + 3-tier elevation + modal-scrim, both light + dark themes with eye-perception-corrected alphas; @supports not (backdrop-filter) fallback collapsing surfaces toward solid (preserves ramp ordering). Authored docs/DESIGN_TOKENS.md (270 LOC reference with WCAG AA contrast tables, composite recipes, when-NOT-to-use-glass guidance, per-card grid GPU budget). AGENTS.md gains a § Visual language section as the new agent-contract surface.
Sub-convoy #2 (liquid-glass-modal-and-surface-primitive) — Brief 1 MERGED. Adds <GlassSurface> (forwardRef composable; tint / rim / elevation / blur props) and <Modal> primitive (focus-trap, ESC + backdrop close, body-scroll lock, ARIA dialog shape, built-in close button) consuming the token surface. lib/use-focus-trap.js — homegrown hook (~60 LOC, no dep). 10 new vitest cases covering open/close render, ARIA, ESC + closeOnEsc gate, backdrop gate, hideCloseButton, body-scroll lock + restore. 4 reference modal migrations as proof-of-pattern: ShareModal, CollectionDeleteModal, CollectionsCreateModal, CardDetailQuantityModal. Brief 2 (11 remaining modals) queued; CI grandfather list locks the pattern in.
Sub-convoy #3 (liquid-glass-form-primitives) — Brief 1 MERGED. Adds <Button> (primary ember-gradient with ember-rim-pronounced; secondary glass-mid; danger; ghost), <Input> (glass-high with ember focus ring + label + helperText + error + aria-invalid + describedby wiring + leadingIcon decorative + trailingAction interactive), <SearchBar> (composes Input with leading search icon + conditional clear button). 10 new vitest cases. pages/login.js + pages/signup.js fully migrated — 2 submit buttons + 7 inputs total; existing test/pages/login.test.js assertion ("Sign in to Deck Hearth" button text) preserved. Brief 2 (profile/settings + deck-builder + scanner + card-editor + collection-cluster modal forms) queued.
Sub-convoy #4 (liquid-glass-layout-shell) — MERGED. 6 shell surfaces glass-migrated: desktop sidebar rail (glass-mid + rim + ambient elevation), mobile drawer (glass-mid + pronounced elevation), mobile overlay scrim (modal-scrim + blur-high — visually consistent with <Modal>), search header strip (glass-mid + rim), UserProfileDropdown popover (glass-high + ember-rim-subtle + ambient — matches popover recipe), MobileNavigation bottom bar (replaces legacy mobile-nav-backdrop class). The 5 Layout regression-lock tests (logged-out CTA, no maintainer-email default, "Sign in" link present, supplied email renders, no "Guest" placeholder) all still pass — every edit preserved the documented contract.
Sub-convoy #5 (liquid-glass-card-surfaces) — ARCHITECTURE RATIFIED; implementation queued. Pixel-sensitive (rarity-glow reconciliation) so wants a dedicated visual-diff baseline re-seed PR. Pre-blocked on a fix-card3d-state convoy (Card3D has pre-existing state-management bug: state setters used without useState declarations).
Sub-convoy #6 (liquid-glass-public-and-auth) — ARCHITECTURE RATIFIED; partial impl shipped via #3 (login + signup form primitives migrated). Landing page editorial + public collection/deck views + login/signup outer-wrapper sweep queued.
Sub-convoy #7 (motion-system-pass) — MERGED. 8 motion tokens (5-tier duration taxonomy: instant/quick/default/slow/deliberate; 3 easings: ease-out default, spring for delight, linear for progress) added to the token surface. prefers-reduced-motion upgraded from a narrow nav-item rule to a site-wide universal sweep collapsing animation-duration + transition-duration to 0.01ms (preserves end states, no flicker); .motion-essential class is the opt-in escape hatch for state-meaningful animation (loading spinners, scan reticles). Authored docs/MOTION_SYSTEM.md with WCAG SC 2.3.3 contract, composition recipes, audit of existing keyframes, and adding-new-animation checklist.
Sub-convoy #8 (cleanup-legacy-design-css) — Brief 1 MERGED. Two new CI jobs in .github/workflows/ci.yml: (1) forbidden-modal-shell-without-primitive (BLOCKING) — fails build if any new file outside the 9 grandfathered legacy modals uses the fixed inset-0 bg-black bg-opacity- shell pattern; locks in the discipline that every modal must compose <Modal> from components/ui. (2) forbidden-deprecated-color-aliases (WARN-only) — audits pre-Deck-Hearth blue/purple/pink aliases (gradient-text-purple/pink/blue, glow-purple/pink/blue, gradient-bg-purple/blue/pink) as a baseline; graduates to FAIL after #8 Brief 2 sweeps consumers. .cursor/rules/ui-and-theming.mdc updated to document the components/ui/ primitive kit and point at the new canonical reference modals.
Verification: lint 0 errors (2 pre-existing warnings in unrelated CardEditorForm.js + CollectionsPageView.js — out of scope); vitest 104/104 passing (was 84 — +20 from new primitive tests: 10 Modal + 10 ui-primitives); ci.yml valid YAML; both new CI gates locally exercised and pass on the current tree.
Operator follow-ups documented in .convoys/ship-readiness.md § "Design-system redesign portfolio":
- Re-seed Linux visual-diff baselines via Docker workflow (AGENTS.md § 6) after this merges.
- preview-smoke.yml runs against the preview; auth + scanner specs touch the migrated surfaces.
- Vercel promote to production once smoke + visual gates pass.
- Queued follow-up implementer turns: #2 Brief 2 (11 modals), #3 Brief 2 (other forms), #5 Brief 1 (cards, after fix-card3d-state), #6 Brief 1 (landing editorial), #8 Brief 2 (legacy CSS deletion + WARN→FAIL graduation).
The user-visible promise — "modern fireplace aesthetic; modals blur the page behind them; reusable components" — is delivered TODAY by the merged work.
Co-authored-by: Cursor <cursoragent@cursor.com>
* fix(use-focus-trap): preserve named useFocusTrap export for ScannerPageView
The portfolio squash inadvertently overwrote the pre-existing
lib/use-focus-trap.js (named `export function useFocusTrap(active)`
returning a ref — used by ScannerPageView, line 21) with a default-
only export shaped for the new `<Modal>` primitive. Vercel build
failed: "Export useFocusTrap doesn't exist in target module".
Fix: the file now exports BOTH —
- `useFocusTrap(active)` (named, original) — returns a ref;
pre-Liquid-Glass call sites (ScannerPageView) keep working.
- `useFocusTrapContainer({ active, containerRef, ... })` (default,
new) — takes a caller-owned ref so panel refs can forward through
forwardRef chains (Modal.js consumes this shape).
Both hooks are commented to document which to use when. Modal.js
imports default already, so no change needed there.
Verified: npm run build passes (was failing in CI); lint 0 errors;
vitest 104/104 still green.
Co-authored-by: Cursor <cursoragent@cursor.com>
---------
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-06-03 21:12:33 -04:00
|
|
|
</>
|
|
|
|
|
</Modal>
|
2025-07-25 23:28:52 -04:00
|
|
|
);
|
feat(design-system): Liquid Glass redesign portfolio — foundation + primitives + Layout (#95)
* feat(design-system): Liquid Glass redesign portfolio — foundation + primitive kit + Layout shell
Operator-requested epic to migrate the UI from the current "warm panel + side-highlight + heavy gradient" visual language to a Liquid Glass aesthetic that retains Deck Hearth's fireplace warmth as accent / gradient / motion (not as panel fill). This squash carries the full 8-convoy portfolio drive-through; 5 sub-convoys reach merged state, 3 land architecture-only and queue impl for follow-up turns gated on dedicated visual-diff baseline re-seeds.
Sub-convoy #1 (liquid-glass-design-tokens) — MERGED. 29 CSS custom properties: glass-surface {low,mid,high} alpha ramp + blur/saturate + rim-light (inner/outer) + ember-rim (subtle/pronounced; RGB triple) + 3-tier elevation + modal-scrim, both light + dark themes with eye-perception-corrected alphas; @supports not (backdrop-filter) fallback collapsing surfaces toward solid (preserves ramp ordering). Authored docs/DESIGN_TOKENS.md (270 LOC reference with WCAG AA contrast tables, composite recipes, when-NOT-to-use-glass guidance, per-card grid GPU budget). AGENTS.md gains a § Visual language section as the new agent-contract surface.
Sub-convoy #2 (liquid-glass-modal-and-surface-primitive) — Brief 1 MERGED. Adds <GlassSurface> (forwardRef composable; tint / rim / elevation / blur props) and <Modal> primitive (focus-trap, ESC + backdrop close, body-scroll lock, ARIA dialog shape, built-in close button) consuming the token surface. lib/use-focus-trap.js — homegrown hook (~60 LOC, no dep). 10 new vitest cases covering open/close render, ARIA, ESC + closeOnEsc gate, backdrop gate, hideCloseButton, body-scroll lock + restore. 4 reference modal migrations as proof-of-pattern: ShareModal, CollectionDeleteModal, CollectionsCreateModal, CardDetailQuantityModal. Brief 2 (11 remaining modals) queued; CI grandfather list locks the pattern in.
Sub-convoy #3 (liquid-glass-form-primitives) — Brief 1 MERGED. Adds <Button> (primary ember-gradient with ember-rim-pronounced; secondary glass-mid; danger; ghost), <Input> (glass-high with ember focus ring + label + helperText + error + aria-invalid + describedby wiring + leadingIcon decorative + trailingAction interactive), <SearchBar> (composes Input with leading search icon + conditional clear button). 10 new vitest cases. pages/login.js + pages/signup.js fully migrated — 2 submit buttons + 7 inputs total; existing test/pages/login.test.js assertion ("Sign in to Deck Hearth" button text) preserved. Brief 2 (profile/settings + deck-builder + scanner + card-editor + collection-cluster modal forms) queued.
Sub-convoy #4 (liquid-glass-layout-shell) — MERGED. 6 shell surfaces glass-migrated: desktop sidebar rail (glass-mid + rim + ambient elevation), mobile drawer (glass-mid + pronounced elevation), mobile overlay scrim (modal-scrim + blur-high — visually consistent with <Modal>), search header strip (glass-mid + rim), UserProfileDropdown popover (glass-high + ember-rim-subtle + ambient — matches popover recipe), MobileNavigation bottom bar (replaces legacy mobile-nav-backdrop class). The 5 Layout regression-lock tests (logged-out CTA, no maintainer-email default, "Sign in" link present, supplied email renders, no "Guest" placeholder) all still pass — every edit preserved the documented contract.
Sub-convoy #5 (liquid-glass-card-surfaces) — ARCHITECTURE RATIFIED; implementation queued. Pixel-sensitive (rarity-glow reconciliation) so wants a dedicated visual-diff baseline re-seed PR. Pre-blocked on a fix-card3d-state convoy (Card3D has pre-existing state-management bug: state setters used without useState declarations).
Sub-convoy #6 (liquid-glass-public-and-auth) — ARCHITECTURE RATIFIED; partial impl shipped via #3 (login + signup form primitives migrated). Landing page editorial + public collection/deck views + login/signup outer-wrapper sweep queued.
Sub-convoy #7 (motion-system-pass) — MERGED. 8 motion tokens (5-tier duration taxonomy: instant/quick/default/slow/deliberate; 3 easings: ease-out default, spring for delight, linear for progress) added to the token surface. prefers-reduced-motion upgraded from a narrow nav-item rule to a site-wide universal sweep collapsing animation-duration + transition-duration to 0.01ms (preserves end states, no flicker); .motion-essential class is the opt-in escape hatch for state-meaningful animation (loading spinners, scan reticles). Authored docs/MOTION_SYSTEM.md with WCAG SC 2.3.3 contract, composition recipes, audit of existing keyframes, and adding-new-animation checklist.
Sub-convoy #8 (cleanup-legacy-design-css) — Brief 1 MERGED. Two new CI jobs in .github/workflows/ci.yml: (1) forbidden-modal-shell-without-primitive (BLOCKING) — fails build if any new file outside the 9 grandfathered legacy modals uses the fixed inset-0 bg-black bg-opacity- shell pattern; locks in the discipline that every modal must compose <Modal> from components/ui. (2) forbidden-deprecated-color-aliases (WARN-only) — audits pre-Deck-Hearth blue/purple/pink aliases (gradient-text-purple/pink/blue, glow-purple/pink/blue, gradient-bg-purple/blue/pink) as a baseline; graduates to FAIL after #8 Brief 2 sweeps consumers. .cursor/rules/ui-and-theming.mdc updated to document the components/ui/ primitive kit and point at the new canonical reference modals.
Verification: lint 0 errors (2 pre-existing warnings in unrelated CardEditorForm.js + CollectionsPageView.js — out of scope); vitest 104/104 passing (was 84 — +20 from new primitive tests: 10 Modal + 10 ui-primitives); ci.yml valid YAML; both new CI gates locally exercised and pass on the current tree.
Operator follow-ups documented in .convoys/ship-readiness.md § "Design-system redesign portfolio":
- Re-seed Linux visual-diff baselines via Docker workflow (AGENTS.md § 6) after this merges.
- preview-smoke.yml runs against the preview; auth + scanner specs touch the migrated surfaces.
- Vercel promote to production once smoke + visual gates pass.
- Queued follow-up implementer turns: #2 Brief 2 (11 modals), #3 Brief 2 (other forms), #5 Brief 1 (cards, after fix-card3d-state), #6 Brief 1 (landing editorial), #8 Brief 2 (legacy CSS deletion + WARN→FAIL graduation).
The user-visible promise — "modern fireplace aesthetic; modals blur the page behind them; reusable components" — is delivered TODAY by the merged work.
Co-authored-by: Cursor <cursoragent@cursor.com>
* fix(use-focus-trap): preserve named useFocusTrap export for ScannerPageView
The portfolio squash inadvertently overwrote the pre-existing
lib/use-focus-trap.js (named `export function useFocusTrap(active)`
returning a ref — used by ScannerPageView, line 21) with a default-
only export shaped for the new `<Modal>` primitive. Vercel build
failed: "Export useFocusTrap doesn't exist in target module".
Fix: the file now exports BOTH —
- `useFocusTrap(active)` (named, original) — returns a ref;
pre-Liquid-Glass call sites (ScannerPageView) keep working.
- `useFocusTrapContainer({ active, containerRef, ... })` (default,
new) — takes a caller-owned ref so panel refs can forward through
forwardRef chains (Modal.js consumes this shape).
Both hooks are commented to document which to use when. Modal.js
imports default already, so no change needed there.
Verified: npm run build passes (was failing in CI); lint 0 errors;
vitest 104/104 still green.
Co-authored-by: Cursor <cursoragent@cursor.com>
---------
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-06-03 21:12:33 -04:00
|
|
|
}
|