From 7b89023d5e0f5070e24bc6ef51dd84971c3249c9 Mon Sep 17 00:00:00 2001 From: Thom Lamb Date: Thu, 20 Aug 2026 10:55:47 -0500 Subject: [PATCH] feat(frontend): show the RD mark beside the storefront wordmark MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Puts the monogram to the left of "Redefined Designs" in the site header. Inline SVG rather than an for two reasons. The tile is drawn in currentColor so it follows the antd text token and inverts with the app's own theme switch — the favicon file inverts on prefers-color-scheme, which tracks the operating system and would disagree with the app whenever someone toggles the theme themselves. And the letters are knocked out with a mask rather than painted in the background colour, so the mark sits correctly on any surface instead of carrying a backdrop that only matches this header. The mask id is generated per instance, since two marks on one page would otherwise share an id and the second would reference the first. It is stripped to alphanumerics because React's generated ids contain colons. Marked aria-hidden: the wordmark beside it already says the name, so announcing it again would be noise. Verified by screenshotting the header in both themes rather than by reasoning about the colours. --- frontend/src/App.tsx | 13 ++++-- frontend/src/components/BrandMark.tsx | 65 +++++++++++++++++++++++++++ frontend/src/styles.css | 13 ++++++ 3 files changed, 88 insertions(+), 3 deletions(-) create mode 100644 frontend/src/components/BrandMark.tsx diff --git a/frontend/src/App.tsx b/frontend/src/App.tsx index 26eadac..55282f6 100755 --- a/frontend/src/App.tsx +++ b/frontend/src/App.tsx @@ -4,6 +4,7 @@ import { ShoppingCartOutlined, FilterOutlined } from '@ant-design/icons'; import { Link, useLocation, useSearchParams } from 'react-router-dom'; import { Item, FilterOptions, fetchItems, fetchFilterOptions } from './api'; import ItemCard from './components/ItemCard'; +import BrandMark from './components/BrandMark'; import FilterDrawer from './components/FilterDrawer'; import ActiveFilterChips from './components/ActiveFilterChips'; import { @@ -118,9 +119,15 @@ export default function App() { borderBottom: `1px solid ${token.colorBorderSecondary}` }} > - - Redefined Designs - + {/* Wrapped so the mark and the wordmark travel together — the title + keeps its own ellipsis behaviour when the header gets narrow, and the + mark is never what gets truncated. */} +
+ + + Redefined Designs + +
diff --git a/frontend/src/components/BrandMark.tsx b/frontend/src/components/BrandMark.tsx new file mode 100644 index 0000000..963dc11 --- /dev/null +++ b/frontend/src/components/BrandMark.tsx @@ -0,0 +1,65 @@ +import { useId } from 'react'; + +interface Props { + size?: number; + className?: string; +} + +/** + * The RD monogram, as an inline SVG rather than an ``. + * + * Two reasons it has to be inline. The tile is drawn in `currentColor`, so it + * follows the antd text token and inverts with the app's own theme switch — the + * favicon file inverts on `prefers-color-scheme`, which tracks the operating + * system and would disagree with the app whenever someone toggles the theme + * themselves. And the letters are knocked out with a mask rather than painted in + * the background colour, so the mark sits correctly on any surface instead of + * carrying a hardcoded backdrop that only matches the header. + * + * Decorative: the header already says "Redefined Designs" beside it, so it is + * hidden from assistive technology rather than read out twice. + */ +export default function BrandMark({ size = 28, className }: Props) { + // Unique per instance — two marks on one page would otherwise share a mask id + // and the second would reference the first. The id is stripped to alphanumerics + // because React's generated ids contain colons. + const maskId = `rd-knockout-${useId().replace(/[^a-zA-Z0-9]/g, '')}`; + + return ( + + ); +} diff --git a/frontend/src/styles.css b/frontend/src/styles.css index 057cfa6..a222c88 100755 --- a/frontend/src/styles.css +++ b/frontend/src/styles.css @@ -45,6 +45,19 @@ body { margin: 0; } line-height: normal; } +.site-header-brand { + display: flex; + align-items: center; + gap: 10px; + flex: 1 1 auto; + min-width: 0; +} + +/* The mark holds its size while the wordmark absorbs the squeeze. */ +.site-header-brand svg { + flex: none; +} + .site-header-title { margin: 0 !important; font-size: clamp(16px, 5vw, 24px); -- 2.54.0