Match the marketing homepage, About page, and folder header to the latest Figma #74
@@ -1,6 +1,7 @@
|
|||||||
"use client";
|
"use client";
|
||||||
|
|
||||||
import { memo } from "react";
|
import { memo } from "react";
|
||||||
|
import NextLink from "next/link";
|
||||||
import Button from "../../buttons/Button";
|
import Button from "../../buttons/Button";
|
||||||
import { contentLockupShapePath, getAssetPath } from "../../../../lib/assetUtils";
|
import { contentLockupShapePath, getAssetPath } from "../../../../lib/assetUtils";
|
||||||
import type { ContentLockupViewProps } from "./ContentLockup.types";
|
import type { ContentLockupViewProps } from "./ContentLockup.types";
|
||||||
@@ -98,14 +99,13 @@ function ContentLockupView({
|
|||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{/* Link for feature variant */}
|
|
||||||
{variant === "feature" && linkText && linkHref && (
|
{variant === "feature" && linkText && linkHref && (
|
||||||
<a
|
<NextLink
|
||||||
href={linkHref}
|
href={linkHref}
|
||||||
className="text-medium-underline underline text-[var(--color-content-default-primary)] hover:text-[var(--color-content-default-secondary)] transition-colors focus:outline-none focus:ring-2 focus:ring-[var(--color-surface-default-brand-royal)] focus:ring-offset-2 focus:ring-offset-[var(--color-surface-default-secondary)] rounded-sm px-1 py-0.5"
|
className="w-fit self-start cursor-pointer text-small-paragraph md:text-medium-paragraph text-[var(--color-content-default-primary)] underline decoration-solid [text-underline-position:from-font] hover:opacity-90 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-[var(--color-border-invert-primary)]"
|
||||||
>
|
>
|
||||||
{linkText}
|
{linkText}
|
||||||
</a>
|
</NextLink>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{/* CTA Button */}
|
{/* CTA Button */}
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ Convention for files served from `public/`. Path helpers live in
|
|||||||
```
|
```
|
||||||
public/
|
public/
|
||||||
assets/
|
assets/
|
||||||
icons/ # UI chrome (alert, close, help, pointer)
|
icons/ # UI chrome (close, help) + FeatureGrid card glyphs
|
||||||
logos/ # Brand + social lockups
|
logos/ # Brand + social lockups
|
||||||
partners/ # Logo wall partner SVGs (kebab org slug)
|
partners/ # Logo wall partner SVGs (kebab org slug)
|
||||||
marketing/ # Hero, feature panels, section numbers, avatars, banners, book cover
|
marketing/ # Hero, feature panels, section numbers, avatars, banners, book cover
|
||||||
@@ -25,7 +25,7 @@ public/
|
|||||||
|
|
||||||
| Location | Used for | Resolution |
|
| Location | Used for | Resolution |
|
||||||
| --- | --- | --- |
|
| --- | --- | --- |
|
||||||
| `public/assets/icons/` | Static chrome served by URL (`icon-close.svg`, `icon-help.svg`) | `ASSETS.ICON_*` |
|
| `public/assets/icons/` | Static chrome served by URL (`icon-close.svg`, `icon-help.svg`) and FeatureGrid card glyphs (`icon-git-branch.svg`, …) | `ASSETS.ICON_*`, `featureIconPath()` |
|
||||||
| `app/components/asset/icon/` | Bundled create-flow / nav SVGs imported by `Icon.tsx` | Webpack import, not `public/` |
|
| `app/components/asset/icon/` | Bundled create-flow / nav SVGs imported by `Icon.tsx` | Webpack import, not `public/` |
|
||||||
|
|
||||||
Do not duplicate the same glyph in both places unless migrating between systems.
|
Do not duplicate the same glyph in both places unless migrating between systems.
|
||||||
@@ -61,7 +61,8 @@ stage. Raster → SVG conversion is tracked in
|
|||||||
| Path | Used by | Disposition |
|
| Path | Used by | Disposition |
|
||||||
| --- | --- | --- |
|
| --- | --- | --- |
|
||||||
| `logos/partners/*.svg` (×6) | LogoWall | **Done** — SVG (kebab org slug, no `logo-` prefix) |
|
| `logos/partners/*.svg` (×6) | LogoWall | **Done** — SVG (kebab org slug, no `logo-` prefix) |
|
||||||
| `marketing/feature-*.svg` (×4) | FeatureGrid | Exported from Figma Section/Feature-Grid (18847:22410) |
|
| `marketing/feature-*.svg` (×4) | Mini cards | Panel art (`support`, `exercises`, `guidance`, `tools`) |
|
||||||
|
| `icons/icon-{git-branch,arrow-left-right,door-open,message-square-share}.svg` | FeatureGrid | Card glyphs from Figma Section/Feature-Grid (18632:10668) |
|
||||||
| `marketing/section-number-*.svg` (×3) | SectionNumber | **Done** — SVG |
|
| `marketing/section-number-*.svg` (×3) | SectionNumber | **Done** — SVG |
|
||||||
| `marketing/avatar-*.svg` (×3) | Avatar / ASSETS | **Done** — SVG |
|
| `marketing/avatar-*.svg` (×3) | Avatar / ASSETS | **Done** — SVG |
|
||||||
| `marketing/hero-image.png` | HeroBanner | **Design review** — likely keep raster |
|
| `marketing/hero-image.png` | HeroBanner | **Design review** — likely keep raster |
|
||||||
|
|||||||
@@ -114,7 +114,7 @@ export function featurePanelPath(key: FeaturePanelKey): string {
|
|||||||
return `assets/marketing/feature-${key}.svg`;
|
return `assets/marketing/feature-${key}.svg`;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Feature-grid card glyphs (Figma **18632:10668**). */
|
/** Feature-grid card glyphs in `public/assets/icons/` (Figma **18632:10668**). */
|
||||||
export type FeatureIconKey =
|
export type FeatureIconKey =
|
||||||
| "git-branch"
|
| "git-branch"
|
||||||
| "arrow-left-right"
|
| "arrow-left-right"
|
||||||
@@ -122,7 +122,7 @@ export type FeatureIconKey =
|
|||||||
| "message-square-share";
|
| "message-square-share";
|
||||||
|
|
||||||
export function featureIconPath(key: FeatureIconKey): string {
|
export function featureIconPath(key: FeatureIconKey): string {
|
||||||
return `assets/marketing/feature-icon-${key}.svg`;
|
return `assets/icons/icon-${key}.svg`;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Case study card artwork in `public/assets/case-study/`. */
|
/** Case study card artwork in `public/assets/case-study/`. */
|
||||||
|
|||||||
|
Before Width: | Height: | Size: 406 B After Width: | Height: | Size: 406 B |
|
Before Width: | Height: | Size: 1018 B After Width: | Height: | Size: 1018 B |
|
Before Width: | Height: | Size: 602 B After Width: | Height: | Size: 602 B |
|
Before Width: | Height: | Size: 800 B After Width: | Height: | Size: 800 B |
@@ -132,6 +132,7 @@ describe("FeatureGrid (behavioral tests)", () => {
|
|||||||
expect(icon).toHaveAttribute("width", "40");
|
expect(icon).toHaveAttribute("width", "40");
|
||||||
expect(icon).toHaveAttribute("height", "40");
|
expect(icon).toHaveAttribute("height", "40");
|
||||||
expect(icon).toHaveAttribute("alt", "");
|
expect(icon).toHaveAttribute("alt", "");
|
||||||
|
expect(icon.getAttribute("src")).toMatch(/^\/assets\/icons\/icon-/);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -63,4 +63,24 @@ describe("ContentLockup", () => {
|
|||||||
expect(description.className).toContain("text-small-paragraph");
|
expect(description.className).toContain("text-small-paragraph");
|
||||||
expect(description.className).toContain("sm:text-large-paragraph");
|
expect(description.className).toContain("sm:text-large-paragraph");
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it("renders the feature lockup CTA as an inline text link", () => {
|
||||||
|
renderWithProviders(
|
||||||
|
<ContentLockup
|
||||||
|
variant="feature"
|
||||||
|
title="Tools for the parts that get hard"
|
||||||
|
subtitle="Every group runs into the same knots."
|
||||||
|
linkText="Explore the toolkit"
|
||||||
|
linkHref="/learn"
|
||||||
|
/>,
|
||||||
|
);
|
||||||
|
|
||||||
|
const link = screen.getByRole("link", { name: "Explore the toolkit" });
|
||||||
|
expect(link).toHaveAttribute("href", "/learn");
|
||||||
|
expect(link.className).toMatch(/underline/);
|
||||||
|
expect(link.className).toMatch(/w-fit/);
|
||||||
|
expect(link.className).not.toMatch(/px-1/);
|
||||||
|
expect(link.className).not.toMatch(/focus:ring-2/);
|
||||||
|
expect(link.className).toMatch(/focus-visible:outline/);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|||||||