feat: Add Logo, Prose, DocsLayout, HeroBackground, HeroSection

This commit is contained in:
Gauthier Daniels 2025-04-19 14:37:41 +02:00
parent 36f0371132
commit cec56f2896
8 changed files with 475 additions and 1 deletions

View File

@ -9,6 +9,7 @@
"@universal-middleware/fastify": "^0.5.16",
"clsx": "^2.1.1",
"fastify": "^5.3.0",
"solid-highlight": "^0.1.26",
"solid-js": "^1.9.5",
"telefunc": "^0.2.3",
"vike": "^0.4.228",
@ -666,6 +667,8 @@
"prettier-linter-helpers": ["prettier-linter-helpers@1.0.0", "", { "dependencies": { "fast-diff": "^1.1.2" } }, "sha512-GbK2cP9nraSSUF9N2XwUwqfzlAFlMNYYl+ShE/V+H8a9uNl/oUqB1w2EL54Jh0OlyRSd8RfWYJ3coVS4TROP2w=="],
"prismjs": ["prismjs@1.30.0", "", {}, "sha512-DEvV2ZF2r2/63V+tK8hQvrR2ZGn10srHbXviTlcv7Kpzw8jWiNTqbVgjO3IY8RxrrOUF8VPMQQFysYYYv0YZxw=="],
"process-warning": ["process-warning@5.0.0", "", {}, "sha512-a39t9ApHNx2L4+HBnQKqxxHNs1r7KF+Intd8Q/g1bUh6q0WIp9voPXJ/x0j+ZL45KF1pJd9+q2jLIRMfvEshkA=="],
"punycode": ["punycode@2.3.1", "", {}, "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg=="],
@ -722,6 +725,8 @@
"sirv": ["sirv@3.0.1", "", { "dependencies": { "@polka/url": "^1.0.0-next.24", "mrmime": "^2.0.0", "totalist": "^3.0.0" } }, "sha512-FoqMu0NCGBLCcAkS1qA+XJIQTR6/JHfQXl+uGteNCQ76T91DMUjPa9xfmeqMY3z80nLSg9yQmNjK0Px6RWsH/A=="],
"solid-highlight": ["solid-highlight@0.1.26", "", { "peerDependencies": { "prismjs": "^1.29.0", "solid-js": "^1.8.0" } }, "sha512-Iw1mi3vE+YCBBBU/+HHc5y8VNULaGXUX4OK2c9TbzegOGCitzW0uNIvb0s3S0KoVv7Uma/KadWHNFXkwZCwPgQ=="],
"solid-js": ["solid-js@1.9.5", "", { "dependencies": { "csstype": "^3.1.0", "seroval": "^1.1.0", "seroval-plugins": "^1.1.0" } }, "sha512-ogI3DaFcyn6UhYhrgcyRAMbu/buBJitYQASZz5WzfQVPP10RD2AbCoRZ517psnezrasyCbWzIxZ6kVqet768xw=="],
"solid-refresh": ["solid-refresh@0.6.3", "", { "dependencies": { "@babel/generator": "^7.23.6", "@babel/helper-module-imports": "^7.22.15", "@babel/types": "^7.23.6" }, "peerDependencies": { "solid-js": "^1.3" } }, "sha512-F3aPsX6hVw9ttm5LYlth8Q15x6MlI/J3Dn+o3EQyRTtTxidepSTwAYdozt01/YA+7ObcciagGEyXIopGZzQtbA=="],

View File

@ -24,7 +24,7 @@ type ButtonProps = {
className?: string;
} & (
| JSX.IntrinsicElements["button"]
| (JSX.IntrinsicElements["a"] & { href?: undefined })
| (JSX.IntrinsicElements["a"] & { href: string })
);
export function Button({

42
app/components/Logo.tsx Normal file
View File

@ -0,0 +1,42 @@
import type { JSX } from "solid-js";
import { createUniqueId } from "solid-js";
function LogomarkPaths() {
const id = createUniqueId();
return (
<>
<defs>
<linearGradient
id={id}
x1="0"
y1="0"
x2="1"
y2="0"
gradientUnits="userSpaceOnUse"
gradientTransform="matrix(12.792,-21.32,-21.32,-12.792,5.208,23.32)"
>
<stop offset="0" stop-color="rgb(43,127,255)" />
<stop offset="1" stop-color="rgb(142,81,255)" />
</linearGradient>
</defs>
<g transform="matrix(-1.76727,0,0,1.76727,49.1089,-3.53454)">
<path
d="M16.161,18.989L20.49,23.32L21.9,21.91L2.1,2.1L0.69,3.51L2.714,5.535L-4.085,11.253L-4.085,13.054L3.185,19.167L4.629,17.337L-1.61,12.165L4.397,7.219L9.588,12.412L6,16L6.01,16.01L6,16.01L6,22L18,22L18,20.83L16.161,18.989ZM14.417,17.244L16,18.83L16,20L8,20L8,16.5L10.837,13.663L14.417,17.244ZM8,4L16,4L16,7.5L13.16,10.34L14.41,11.59L18,8.01L17.99,8L18,8L18,2L6,2L6,3.17L8,5.17L8,4ZM25.294,12.164L19.071,17.34L20.542,19.164L27.788,13.075L27.788,11.274L20.597,5.22L19.158,7.075L25.294,12.164Z"
style={{ fill: `url(#${id})` }}
/>
</g>
</>
);
}
export function Logo(props: JSX.IntrinsicElements["svg"]) {
return (
<svg view-box="0 0 58 38" {...props}>
<title>Memento Dev</title>
<LogomarkPaths />
</svg>
);
}

36
app/components/Prose.tsx Normal file
View File

@ -0,0 +1,36 @@
import type { JSX } from "solid-js";
import { Dynamic } from "solid-js/web";
import clsx from "clsx";
type ProseProps = JSX.IntrinsicElements["div"] & {
class?: string;
as?: keyof JSX.IntrinsicElements;
};
export function Prose(props: ProseProps) {
const Component = props.as ?? "div";
return (
<Dynamic
component={Component}
class={clsx(
props.class,
"prose max-w-none prose-slate dark:text-slate-400 dark:prose-invert",
// headings
"prose-headings:scroll-mt-28 prose-headings:font-display prose-headings:font-normal lg:prose-headings:scroll-mt-[8.5rem]",
// lead
"prose-lead:text-slate-500 dark:prose-lead:text-slate-400",
// links
"prose-a:font-semibold dark:prose-a:text-violet-400",
// link underline
"dark:[--tw-prose-background:var(--color-slate-900)] prose-a:no-underline prose-a:shadow-[inset_0_-2px_0_0_var(--tw-prose-background,#fff),inset_0_calc(-1*(var(--tw-prose-underline-size,4px)+2px))_0_0_var(--tw-prose-underline,var(--color-violet-300))] prose-a:hover:[--tw-prose-underline-size:6px] dark:prose-a:shadow-[inset_0_calc(-1*var(--tw-prose-underline-size,2px))_0_0_var(--tw-prose-underline,var(--color-violet-800))] dark:prose-a:hover:[--tw-prose-underline-size:6px]",
// pre
"prose-pre:rounded-xl prose-pre:bg-slate-900 prose-pre:shadow-lg dark:prose-pre:bg-slate-800/60 dark:prose-pre:ring-1 dark:prose-pre:shadow-none dark:prose-pre:ring-slate-300/10",
// hr
"dark:prose-hr:border-slate-800",
)}
{...props}
/>
);
}

View File

@ -0,0 +1,39 @@
import { type Node } from "@markdoc/markdoc";
import { TableOfContents } from "@syntax/TableOfContents";
import { PrevNextLinks } from "@syntax/PrevNextLinks";
import { collectSections } from "@/lib/sections";
import { DocsHeader } from "@syntax/DocsHeader";
import { Prose } from "@syntax/Prose";
import React from "react";
export function DocsLayout({
children,
frontmatter: { title },
estimatedReadingTime,
nodes,
}: {
children: React.ReactNode;
frontmatter: { title?: string };
estimatedReadingTime?: string;
nodes: Array<Node>;
}) {
const tableOfContents = collectSections(nodes);
return (
<>
<div className="max-w-2xl min-w-0 flex-auto px-4 py-16 lg:max-w-none lg:pr-0 lg:pl-8 xl:px-16 grow">
<article>
<DocsHeader
title={title}
estimatedReadingTime={estimatedReadingTime}
/>
<Prose>{children}</Prose>
</article>
<PrevNextLinks />
</div>
<TableOfContents tableOfContents={tableOfContents} />
</>
);
}

View File

@ -0,0 +1,190 @@
import type { JSX } from "solid-js";
import { createUniqueId } from "solid-js";
export function HeroBackground(props: JSX.IntrinsicElements["svg"]) {
const id = createUniqueId();
return (
<svg
aria-hidden="true"
viewBox="0 0 668 1069"
width={668}
height={1069}
fill="none"
{...props}
>
<defs>
<clipPath id={`${id}-clip-path`}>
<path
fill="#fff"
transform="rotate(-180 334 534.4)"
d="M0 0h668v1068.8H0z"
/>
</clipPath>
</defs>
<g opacity=".4" clip-path={`url(#${id}-clip-path)`} stroke-width={4}>
<path
opacity=".3"
d="M584.5 770.4v-474M484.5 770.4v-474M384.5 770.4v-474M283.5 769.4v-474M183.5 768.4v-474M83.5 767.4v-474"
stroke="#334155"
/>
<path
d="M83.5 221.275v6.587a50.1 50.1 0 0 0 22.309 41.686l55.581 37.054a50.102 50.102 0 0 1 22.309 41.686v6.587M83.5 716.012v6.588a50.099 50.099 0 0 0 22.309 41.685l55.581 37.054a50.102 50.102 0 0 1 22.309 41.686v6.587M183.7 584.5v6.587a50.1 50.1 0 0 0 22.31 41.686l55.581 37.054a50.097 50.097 0 0 1 22.309 41.685v6.588M384.101 277.637v6.588a50.1 50.1 0 0 0 22.309 41.685l55.581 37.054a50.1 50.1 0 0 1 22.31 41.686v6.587M384.1 770.288v6.587a50.1 50.1 0 0 1-22.309 41.686l-55.581 37.054A50.099 50.099 0 0 0 283.9 897.3v6.588"
stroke="#334155"
/>
<path
d="M384.1 770.288v6.587a50.1 50.1 0 0 1-22.309 41.686l-55.581 37.054A50.099 50.099 0 0 0 283.9 897.3v6.588M484.3 594.937v6.587a50.1 50.1 0 0 1-22.31 41.686l-55.581 37.054A50.1 50.1 0 0 0 384.1 721.95v6.587M484.3 872.575v6.587a50.1 50.1 0 0 1-22.31 41.686l-55.581 37.054a50.098 50.098 0 0 0-22.309 41.686v6.582M584.501 663.824v39.988a50.099 50.099 0 0 1-22.31 41.685l-55.581 37.054a50.102 50.102 0 0 0-22.309 41.686v6.587M283.899 945.637v6.588a50.1 50.1 0 0 1-22.309 41.685l-55.581 37.05a50.12 50.12 0 0 0-22.31 41.69v6.59M384.1 277.637c0 19.946 12.763 37.655 31.686 43.962l137.028 45.676c18.923 6.308 31.686 24.016 31.686 43.962M183.7 463.425v30.69c0 21.564 13.799 40.709 34.257 47.529l134.457 44.819c18.922 6.307 31.686 24.016 31.686 43.962M83.5 102.288c0 19.515 13.554 36.412 32.604 40.645l235.391 52.309c19.05 4.234 32.605 21.13 32.605 40.646M83.5 463.425v-58.45M183.699 542.75V396.625M283.9 1068.8V945.637M83.5 363.225v-141.95M83.5 179.524v-77.237M83.5 60.537V0M384.1 630.425V277.637M484.301 830.824V594.937M584.5 1068.8V663.825M484.301 555.275V452.988M584.5 622.075V452.988M384.1 728.537v-56.362M384.1 1068.8v-20.88M384.1 1006.17V770.287M283.9 903.888V759.85M183.699 1066.71V891.362M83.5 1068.8V716.012M83.5 674.263V505.175"
stroke="#334155"
/>
<circle
cx="83.5"
cy="384.1"
r="10.438"
transform="rotate(-180 83.5 384.1)"
fill="#1E293B"
stroke="#334155"
/>
<circle
cx="83.5"
cy="200.399"
r="10.438"
transform="rotate(-180 83.5 200.399)"
stroke="#334155"
/>
<circle
cx="83.5"
cy="81.412"
r="10.438"
transform="rotate(-180 83.5 81.412)"
stroke="#334155"
/>
<circle
cx="183.699"
cy="375.75"
r="10.438"
transform="rotate(-180 183.699 375.75)"
fill="#1E293B"
stroke="#334155"
/>
<circle
cx="183.699"
cy="563.625"
r="10.438"
transform="rotate(-180 183.699 563.625)"
fill="#1E293B"
stroke="#334155"
/>
<circle
cx="384.1"
cy="651.3"
r="10.438"
transform="rotate(-180 384.1 651.3)"
fill="#1E293B"
stroke="#334155"
/>
<circle
cx="484.301"
cy="574.062"
r="10.438"
transform="rotate(-180 484.301 574.062)"
fill="#0EA5E9"
fill-opacity=".42"
stroke="#0EA5E9"
/>
<circle
cx="384.1"
cy="749.412"
r="10.438"
transform="rotate(-180 384.1 749.412)"
fill="#1E293B"
stroke="#334155"
/>
<circle
cx="384.1"
cy="1027.05"
r="10.438"
transform="rotate(-180 384.1 1027.05)"
stroke="#334155"
/>
<circle
cx="283.9"
cy="924.763"
r="10.438"
transform="rotate(-180 283.9 924.763)"
stroke="#334155"
/>
<circle
cx="183.699"
cy="870.487"
r="10.438"
transform="rotate(-180 183.699 870.487)"
stroke="#334155"
/>
<circle
cx="283.9"
cy="738.975"
r="10.438"
transform="rotate(-180 283.9 738.975)"
fill="#1E293B"
stroke="#334155"
/>
<circle
cx="83.5"
cy="695.138"
r="10.438"
transform="rotate(-180 83.5 695.138)"
fill="#1E293B"
stroke="#334155"
/>
<circle
cx="83.5"
cy="484.3"
r="10.438"
transform="rotate(-180 83.5 484.3)"
fill="#0EA5E9"
fill-opacity=".42"
stroke="#0EA5E9"
/>
<circle
cx="484.301"
cy="432.112"
r="10.438"
transform="rotate(-180 484.301 432.112)"
fill="#1E293B"
stroke="#334155"
/>
<circle
cx="584.5"
cy="432.112"
r="10.438"
transform="rotate(-180 584.5 432.112)"
fill="#1E293B"
stroke="#334155"
/>
<circle
cx="584.5"
cy="642.95"
r="10.438"
transform="rotate(-180 584.5 642.95)"
fill="#1E293B"
stroke="#334155"
/>
<circle
cx="484.301"
cy="851.699"
r="10.438"
transform="rotate(-180 484.301 851.699)"
stroke="#334155"
/>
<circle
cx="384.1"
cy="256.763"
r="10.438"
transform="rotate(-180 384.1 256.763)"
stroke="#334155"
/>
</g>
</svg>
);
}

161
app/layouts/HeroSection.tsx Normal file
View File

@ -0,0 +1,161 @@
import type { JSX } from "solid-js";
import blurIndigoImage from "@/images/blur-indigo.webp";
import blurCyanImage from "@/images/blur-cyan.webp";
import { HeroBackground } from "./HeroBackground";
import { Button } from "@/components/Button";
import { Highlight } from "solid-highlight";
import { Image } from "@/components/Image";
import { For } from "solid-js";
import clsx from "clsx";
const codeLanguage = "javascript";
const code = `export default {
role: 'developer',
qualifications: [
'DWWM',
'CDA',
'CDUI',
]
}`;
const tabs = [
{ name: "memento-dev.config.js", isActive: true },
{ name: "package.json", isActive: false },
];
function TrafficLightsIcon(props: JSX.IntrinsicElements["svg"]) {
return (
<svg aria-hidden="true" viewBox="0 0 42 10" fill="none" {...props}>
<circle cx="5" cy="5" r="4.5" />
<circle cx="21" cy="5" r="4.5" />
<circle cx="37" cy="5" r="4.5" />
</svg>
);
}
export function Hero() {
return (
<div class="overflow-hidden bg-slate-900 dark:mt-[-4.75rem] dark:-mb-32 dark:pt-[4.75rem] dark:pb-32">
<div class="py-16 sm:px-2 lg:relative lg:px-0 lg:py-20">
<div class="mx-auto grid max-w-2xl w-full grid-cols-1 items-center gap-x-8 gap-y-16 px-4 lg:max-w-8xl lg:grid-cols-2 lg:px-8 xl:gap-x-16 xl:px-12">
<div class="relative z-10 md:text-center lg:text-left">
<Image
class="absolute right-full bottom-full -mr-72 -mb-56 opacity-50"
src={blurCyanImage}
alt=""
width={530}
height={530}
/>
<div class="relative">
<p class="inline bg-linear-to-r from-indigo-200 via-violet-400 to-indigo-200 bg-clip-text font-display text-5xl tracking-tight text-transparent">
Souviens-toi que tu développeras.
</p>
<p class="mt-3 text-2xl tracking-tight text-slate-400">
Découvrez des ressources essentielles pour améliorer tes
compétences en développement.
</p>
<div class="mt-8 flex gap-4 md:justify-center lg:justify-start">
<Button href="/docs">Accédez aux ressources</Button>
<Button
href="https://github.com/GauthierWebDev/memento-dev"
variant="secondary"
>
Voir sur Github
</Button>
</div>
</div>
</div>
<div class="relative lg:static xl:pl-10">
<div class="absolute inset-x-[-50vw] -top-32 -bottom-48 [mask-image:linear-gradient(transparent,white,white)] lg:-top-32 lg:right-0 lg:-bottom-32 lg:left-[calc(50%+14rem)] lg:[mask-image:none] dark:[mask-image:linear-gradient(transparent,white,transparent)] lg:dark:[mask-image:linear-gradient(white,white,transparent)]">
<HeroBackground class="absolute top-1/2 left-1/2 -translate-x-1/2 -translate-y-1/2 lg:left-0 lg:translate-x-0 lg:translate-y-[-60%]" />
</div>
<div class="relative">
<Image
class="absolute -top-64 -right-64"
src={blurCyanImage}
alt=""
width={530}
height={530}
/>
<Image
class="absolute -right-44 -bottom-40"
src={blurIndigoImage}
alt=""
width={567}
height={567}
/>
<div class="absolute inset-0 rounded-2xl bg-linear-to-tr from-violet-300 via-violet-300/70 to-purple-300 opacity-10 blur-lg" />
<div class="absolute inset-0 rounded-2xl bg-linear-to-tr from-violet-300 via-violet-300/70 to-purple-300 opacity-10" />
<div class="relative rounded-2xl bg-[#0A101F]/80 ring-1 ring-white/10 backdrop-blur-sm">
<div class="absolute -top-px right-11 left-20 h-px bg-linear-to-r from-violet-300/0 via-violet-300/70 to-violet-300/0" />
<div class="absolute right-20 -bottom-px left-11 h-px bg-linear-to-r from-purple-400/0 via-purple-400 to-purple-400/0" />
<div class="pt-4 pl-4">
<TrafficLightsIcon class="h-2.5 w-auto stroke-slate-500/30" />
<div class="mt-4 flex space-x-2 text-xs">
<For each={tabs}>
{(tab) => (
<div
class={clsx(
"flex h-6 rounded-full",
tab.isActive
? "bg-linear-to-r from-violet-400/30 via-violet-400 to-violet-400/30 p-px font-medium text-violet-300"
: "text-slate-500",
)}
>
<div
class={clsx(
"flex items-center rounded-full px-2.5",
tab.isActive && "bg-slate-800",
)}
>
{tab.name}
</div>
</div>
)}
</For>
</div>
<div class="mt-6 flex items-start px-1 text-sm">
<div
aria-hidden="true"
class="border-r border-slate-300/5 pr-4 font-mono text-slate-600 select-none"
>
<For
each={Array.from({ length: code.split("\n").length })}
>
{(_, index) => (
<>
{(index() + 1).toString().padStart(2, "0")}
<br />
</>
)}
</For>
</div>
<Highlight language={codeLanguage}>{code}</Highlight>
{/* <Highlight code={code} language={codeLanguage} theme={{ plain: {}, styles: [] }}>
{({ class, style, tokens, getLineProps, getTokenProps }) => (
<pre class={clsx(class, "flex overflow-x-auto pb-6")} style={style}>
<code class="px-4">
{tokens.map((line, lineIndex) => (
<div key={lineIndex} {...getLineProps({ line })}>
{line.map((token, tokenIndex) => (
<span key={tokenIndex} {...getTokenProps({ token })} />
))}
</div>
))}
</code>
</pre>
)}
</Highlight> */}
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
);
}

View File

@ -14,6 +14,7 @@
"@universal-middleware/fastify": "^0.5.16",
"clsx": "^2.1.1",
"fastify": "^5.3.0",
"solid-highlight": "^0.1.26",
"solid-js": "^1.9.5",
"telefunc": "^0.2.3",
"vike": "^0.4.228",