Give every public route its own description, and keep canonicals and structured data on the host that serves the page.
Decorative images on the marketing pages stay quiet, and every dialog close button is named Close. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
1 parent
0b6e8b00dd
commit
c180e79c38
52 files changed
+363
-209
No files matched your search
@@ -14,6 +14,7 @@ import Button from "../../buttons/Button";
|
||||
import AvatarContainer from "../../asset/AvatarContainer";
|
||||
import Avatar from "../../asset/Avatar";
|
||||
import { getAssetPath, ASSETS } from "../../../../lib/assetUtils";
|
||||
import { PRODUCTION_SITE_ORIGIN, websiteJsonLd } from "../../../../lib/siteMetadata";
|
||||
import { prepareFreshCreateFlowEntrySync } from "../../../(app)/create/utils/prepareFreshCreateFlowEntry";
|
||||
import { TopView } from "./Top.view";
|
||||
import type { TopProps } from "./Top.types";
|
||||
@@ -24,10 +25,10 @@ export const avatarImageSources = [
|
||||
getAssetPath(ASSETS.AVATAR_1),
|
||||
] as const;
|
||||
|
||||
/** @deprecated Use `avatarImageSources` — alts are resolved in `TopContainer` via `topNav` messages. */
|
||||
export const avatarImages = avatarImageSources.map((src, index) => ({
|
||||
/** @deprecated Use `avatarImageSources`. The portraits are decorative. */
|
||||
export const avatarImages = avatarImageSources.map((src) => ({
|
||||
src,
|
||||
alt: `Avatar ${3 - index}`,
|
||||
alt: "",
|
||||
}));
|
||||
|
||||
/** Padding/type that tracks lg/xl Menu sizes without cloning the items per breakpoint. */
|
||||
@@ -44,12 +45,17 @@ const FOLDER_CREATE_RULE_RESPONSIVE_CLASS =
|
||||
"md:p-[var(--space-200)] md:gap-[var(--space-050)] md:text-x-small-label lg:p-[var(--space-300)] lg:gap-[var(--space-150)] lg:text-medium-label xl:p-[var(--space-400)] xl:gap-[var(--space-200)] xl:text-x-large-label";
|
||||
|
||||
const TopContainer = memo<TopProps>(
|
||||
({ folderTop = false, loggedIn = false, profile = false, logIn = true }) => {
|
||||
({
|
||||
folderTop = false,
|
||||
loggedIn = false,
|
||||
profile = false,
|
||||
logIn = true,
|
||||
siteOrigin = PRODUCTION_SITE_ORIGIN,
|
||||
}) => {
|
||||
const pathname = usePathname();
|
||||
const router = useRouter();
|
||||
const { openLogin } = useAuthModal();
|
||||
const t = useTranslation("header");
|
||||
const tTopNav = useTranslation("topNav");
|
||||
|
||||
/**
|
||||
* `Top` is hidden on `/create` routes by ConditionalNavigationClient, so
|
||||
@@ -64,20 +70,7 @@ const TopContainer = memo<TopProps>(
|
||||
router.push("/create/informational");
|
||||
}, [loggedIn, router]);
|
||||
|
||||
const schemaData = {
|
||||
"@context": "https://schema.org",
|
||||
"@type": "WebSite",
|
||||
name: "CommunityRule",
|
||||
url: "https://communityrule.com",
|
||||
...(folderTop && {
|
||||
description: tTopNav("schemaDescription"),
|
||||
}),
|
||||
potentialAction: {
|
||||
"@type": "SearchAction",
|
||||
target: "https://communityrule.com/search?q={search_term_string}",
|
||||
"query-input": "required name=search_term_string",
|
||||
},
|
||||
};
|
||||
const schemaData = websiteJsonLd(siteOrigin);
|
||||
|
||||
const logoSize = folderTop ? "topNavFolderTop" : "topNavHeader";
|
||||
|
||||
@@ -188,11 +181,11 @@ const TopContainer = memo<TopProps>(
|
||||
: "max-sm:hidden lg:-space-x-[var(--space-250)] xl:-space-x-[13px]"
|
||||
}
|
||||
>
|
||||
{avatarImageSources.map((src, index) => (
|
||||
{avatarImageSources.map((src) => (
|
||||
<Avatar
|
||||
key={src}
|
||||
src={src}
|
||||
alt={tTopNav(`avatarAlts.${3 - index}`)}
|
||||
alt=""
|
||||
size="small"
|
||||
className={
|
||||
folderTop
|
||||
|
||||
@@ -7,6 +7,8 @@ export interface TopProps {
|
||||
folderTop?: boolean;
|
||||
profile?: boolean;
|
||||
logIn?: boolean;
|
||||
/** Absolute origin for the WebSite JSON-LD block. */
|
||||
siteOrigin?: string;
|
||||
}
|
||||
|
||||
export interface TopViewProps {
|
||||
@@ -15,16 +17,10 @@ export interface TopViewProps {
|
||||
profile: boolean;
|
||||
logIn: boolean;
|
||||
schemaData: {
|
||||
"@context": string;
|
||||
"@type": string;
|
||||
"@context": "https://schema.org";
|
||||
"@type": "WebSite";
|
||||
name: string;
|
||||
url: string;
|
||||
description?: string;
|
||||
potentialAction: {
|
||||
"@type": string;
|
||||
target: string;
|
||||
"query-input": string;
|
||||
};
|
||||
};
|
||||
logoSize: "topNavFolderTop" | "topNavHeader";
|
||||
renderNavigationItems: () => React.ReactNode;
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
"use client";
|
||||
|
||||
import { memo } from "react";
|
||||
import Script from "next/script";
|
||||
import { useTranslation } from "../../../contexts/MessagesContext";
|
||||
import { ASSETS, getAssetPath } from "../../../../lib/assetUtils";
|
||||
import Menu from "../Menu";
|
||||
@@ -32,8 +31,7 @@ function TopView({
|
||||
if (folderTop) {
|
||||
return (
|
||||
<>
|
||||
<Script
|
||||
id="top-navigation-schema"
|
||||
<script
|
||||
type="application/ld+json"
|
||||
dangerouslySetInnerHTML={{ __html: JSON.stringify(schemaData) }}
|
||||
/>
|
||||
|
||||
Reference in new issue
Block a user