diff --git a/src/components/brand.test.tsx b/src/components/brand.test.tsx
index 4baf894..27ade69 100644
--- a/src/components/brand.test.tsx
+++ b/src/components/brand.test.tsx
@@ -72,6 +72,30 @@ describe("brand components", () => {
await expectNoAxeViolations(container);
});
+ it("overrides only the wordmark text with label, keeping the theme symbol", async () => {
+ const { container } = render(
+
+
+ ,
+ );
+ // The product name renders in place of the default "Mind" wordmark...
+ expect(screen.getByText("Drive")).toBeInTheDocument();
+ expect(screen.queryByText("Mind")).not.toBeInTheDocument();
+ // ...in the same serif lockup, and the shared mark is still painted.
+ expect(screen.getByText("Drive")).toHaveClass("font-serif");
+ expect(container.querySelector("img")).toBeInTheDocument();
+ await expectNoAxeViolations(container);
+ });
+
+ it("defaults the wordmark to the theme label when no label is given", () => {
+ render(
+
+
+ ,
+ );
+ expect(screen.getByText("Mind")).toBeInTheDocument();
+ });
+
it("drops the wordmark with symbolOnly", () => {
render(
diff --git a/src/components/brand.tsx b/src/components/brand.tsx
index 67626a5..f3d152a 100644
--- a/src/components/brand.tsx
+++ b/src/components/brand.tsx
@@ -22,6 +22,13 @@ export type SymbolProps = Omit, "src"
export type LogoProps = React.HTMLAttributes & {
/** Render only the symbol, dropping the wordmark text. */
symbolOnly?: boolean;
+ /**
+ * Wordmark text. Defaults to the active theme's `label` ("Mind"). Pass the
+ * product name (e.g. `"Drive"`) so an app's lockup reads as the shared mark +
+ * its own name, set in the same brand serif. Overrides only the text โ the
+ * symbol/mark still comes from the active theme.
+ */
+ label?: string;
};
/**
@@ -32,15 +39,16 @@ export type LogoProps = React.HTMLAttributes & {
* fully bespoke wordmark image via its `logo` asset; when present that image is
* used as-is instead.
*/
-export function Logo({ className, symbolOnly, ...props }: LogoProps) {
+export function Logo({ className, symbolOnly, label, ...props }: LogoProps) {
const { theme, resolvedMode } = useMindTheme();
const mode = pickMode(resolvedMode);
+ const wordmark = label ?? theme.label;
const custom = theme.logo?.[mode];
if (custom) {
return (
);
}
@@ -53,7 +61,7 @@ export function Logo({ className, symbolOnly, ...props }: LogoProps) {
) : null}
{symbolOnly ? null : (
- {theme.label}
+ {wordmark}
)}
diff --git a/stories/foundations/Logo.mdx b/stories/foundations/Logo.mdx
index 094c498..7fbe06c 100644
--- a/stories/foundations/Logo.mdx
+++ b/stories/foundations/Logo.mdx
@@ -41,6 +41,23 @@ import { Logo, Symbol } from "@mind-studio/ui";
+## ๐ท๏ธ Per-app wordmark
+
+Every Mind product **shares the one weave mark** but signs itself with **its own
+name** โ the mark + "Drive", "Slides", "Chat", and so on โ set in the same Fraunces
+serif, weight, and size as the master wordmark. Pass the product name via `label`;
+it overrides **only the text**, never the artwork. With no `label`, the lockup falls
+back to the active brand's name ("Mind"), so the host shell and landing stay
+unchanged.
+
+```tsx
+ {/* mark + "Drive" in the brand serif */}
+ {/* mark + "Slides" */}
+ {/* mark + "Mind" (default) */}
+```
+
+
+
## ๐ Construction
The mark is drawn on a circular grid โ every node shares one unit radius and a
diff --git a/stories/foundations/Logo.stories.tsx b/stories/foundations/Logo.stories.tsx
index da775b8..18cbb6c 100644
--- a/stories/foundations/Logo.stories.tsx
+++ b/stories/foundations/Logo.stories.tsx
@@ -46,7 +46,7 @@ function Caption({ children }: { children: React.ReactNode }) {
* renders in the real typeface rather than a baked image. `ink` fixes the text
* colour for the panel it sits on.
*/
-function Wordmark({ ink, size = 40 }: { ink: string; size?: number }) {
+function Wordmark({ ink, size = 40, text = "Mind" }: { ink: string; size?: number; text?: string }) {
return (
@@ -54,7 +54,7 @@ function Wordmark({ ink, size = 40 }: { ink: string; size?: number }) {
className="font-serif font-semibold leading-none tracking-tight"
style={{ fontSize: size * 0.82, color: ink }}
>
- Mind
+ {text}
);
@@ -108,6 +108,32 @@ export const Lockups: Story = {
),
};
+/* ------------------------------------------------------- product wordmarks */
+
+const PRODUCTS = ["Mind", "Drive", "Slides", "Chat", "Calendar", "Videos"] as const;
+
+export const ProductLockups: Story = {
+ tags: ["!dev"],
+ render: () => (
+
+
+
+ {PRODUCTS.map((p) => (
+
+ ))}
+
+
+
+
+ {PRODUCTS.map((p) => (
+
+ ))}
+
+
+
+ ),
+};
+
/* ------------------------------------------------------------ construction */
export const Construction: Story = {