Treat the feature-grid toolkit control as underlined text and keep its card glyphs with the other URL-served icons.
Co-authored-by: Cursor <cursoragent@cursor.com>
@@ -1,6 +1,7 @@
|
||||
"use client";
|
||||
|
||||
import { memo } from "react";
|
||||
import NextLink from "next/link";
|
||||
import Button from "../../buttons/Button";
|
||||
import { contentLockupShapePath, getAssetPath } from "../../../../lib/assetUtils";
|
||||
import type { ContentLockupViewProps } from "./ContentLockup.types";
|
||||
@@ -98,14 +99,13 @@ function ContentLockupView({
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Link for feature variant */}
|
||||
{variant === "feature" && linkText && linkHref && (
|
||||
<a
|
||||
<NextLink
|
||||
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}
|
||||
</a>
|
||||
</NextLink>
|
||||
)}
|
||||
|
||||
{/* CTA Button */}
|
||||
|
||||
@@ -8,7 +8,7 @@ Convention for files served from `public/`. Path helpers live in
|
||||
```
|
||||
public/
|
||||
assets/
|
||||
icons/ # UI chrome (alert, close, help, pointer)
|
||||
icons/ # UI chrome (close, help) + FeatureGrid card glyphs
|
||||
logos/ # Brand + social lockups
|
||||
partners/ # Logo wall partner SVGs (kebab org slug)
|
||||
marketing/ # Hero, feature panels, section numbers, avatars, banners, book cover
|
||||
@@ -25,7 +25,7 @@ public/
|
||||
|
||||
| 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/` |
|
||||
|
||||
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 |
|
||||
| --- | --- | --- |
|
||||
| `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/avatar-*.svg` (×3) | Avatar / ASSETS | **Done** — SVG |
|
||||
| `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`;
|
||||
}
|
||||
|
||||
/** Feature-grid card glyphs (Figma **18632:10668**). */
|
||||
/** Feature-grid card glyphs in `public/assets/icons/` (Figma **18632:10668**). */
|
||||
export type FeatureIconKey =
|
||||
| "git-branch"
|
||||
| "arrow-left-right"
|
||||
@@ -122,7 +122,7 @@ export type FeatureIconKey =
|
||||
| "message-square-share";
|
||||
|
||||
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/`. */
|
||||
|
||||
|
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("height", "40");
|
||||
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("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/);
|
||||
});
|
||||
});
|
||||
|
||||