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);