Skip to content

Commit f467224

Browse files
authored
Merge pull request #6 from PHS-TSA/www
Implement
2 parents f92d6cc + 65b5d21 commit f467224

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

49 files changed

+3377
-102
lines changed

.github/workflows/deno.yaml

+1
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,7 @@ jobs:
6565
components/
6666
islands/
6767
routes/
68+
sdk/
6869
static/
6970
utils/
7071
vendor/

biome.jsonc

+5-6
Original file line numberDiff line numberDiff line change
@@ -18,20 +18,19 @@
1818
"noHeadElement": "off",
1919
"noHeadImportInDocument": "off",
2020
"noNestedTernary": "off",
21-
"useComponentExportOnlyModules": {
22-
"level": "warn",
23-
"options": { "allowConstantExport": true }
24-
},
21+
"useComponentExportOnlyModules": "off",
2522
"useExplicitType": "off",
2623
"useImportRestrictions": "off",
27-
"noSecrets": "off"
24+
"noSecrets": "off",
25+
"useConsistentCurlyBraces": "off"
2826
},
2927
"style": {
3028
"noDefaultExport": "off",
3129
"noImplicitBoolean": "off",
3230
"useDefaultSwitchClause": "off",
3331
"useFilenamingConvention": "off",
34-
"useNamingConvention": "off"
32+
"useNamingConvention": "off",
33+
"useBlockStatements": "off"
3534
},
3635
"complexity": {
3736
"useLiteralKeys": "off"

components/Footer.tsx

+70
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
import { clsx } from "clsx";
2+
import type { JSX } from "preact";
3+
import { siteName, slogan } from "../utils/site.ts";
4+
5+
/**
6+
* Properties for the {@linkcode Footer} component.
7+
*/
8+
export type FooterProps = JSX.HTMLAttributes<HTMLElement>;
9+
10+
/**
11+
* Render a footer component, which is used as a footer for pages.
12+
* It contains an about section, a categories section, and a "made with" section.
13+
* The about section contains the site name and slogan.
14+
* The categories section contains links to the various pages of the site.
15+
* The "made with" section contains links to the tools that were used to build the site.
16+
*
17+
* @param props - The component's properties.
18+
* @param props.class - The CSS classes to apply to this component.
19+
* @returns The rendered footer component.
20+
*/
21+
export function Footer(props: FooterProps): JSX.Element {
22+
return (
23+
<footer
24+
{...props}
25+
class={clsx(
26+
"z-40 grid w-full max-w-screen-xlg grid-flow-col grid-cols-footer-mobile grid-rows-footer-mobile gap-x-2 gap-y-16 bg-green-950 p-8 text-sm shadow-2xl sm:grid-rows-footer-desktop sm:gap-x-8 md:grid-cols-footer-desktop md:gap-16 dark:bg-green-950",
27+
props.class,
28+
)}
29+
>
30+
<div class="col-start-1 col-end-2 row-start-1 row-end-2">
31+
<Who />
32+
</div>
33+
34+
<div class="col-start-2 col-end-3 row-start-1 row-end-2">
35+
<References />
36+
</div>
37+
</footer>
38+
);
39+
}
40+
41+
/**
42+
* Render the "who" section of the footer.
43+
* It contains the site name and slogan.
44+
*
45+
* @returns The rendered "who" section of the footer.
46+
*/
47+
function Who(): JSX.Element {
48+
return (
49+
<>
50+
<div class="flex flex-row items-center gap-1">
51+
<div class="font-bold text-slate-50 text-xl sm:text-2xl">
52+
{siteName}
53+
</div>
54+
</div>
55+
<div class="text-balance text-slate-400 dark:text-slate-400">
56+
{slogan}
57+
</div>
58+
</>
59+
);
60+
}
61+
62+
function References(): JSX.Element {
63+
return (
64+
<>
65+
<div class="text-balance text-slate-400 dark:text-slate-400">
66+
<a href="/references">References</a>
67+
</div>
68+
</>
69+
);
70+
}

components/Header.tsx

+38
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
import type { ComponentChildren, JSX } from "preact";
2+
import { siteName } from "../utils/site.ts";
3+
4+
export function Header(): JSX.Element {
5+
return (
6+
<nav class="flex flex-col gap-5 bg-green-500 px-10 py-5 md:flex-row dark:bg-green-800">
7+
<a href="/" class="text-pretty font-extrabold text-3xl">
8+
{siteName}
9+
</a>
10+
11+
{/* Force them apart. */}
12+
<span class="hidden flex-grow md:block" />
13+
14+
<nav class="flex flex-row flex-wrap gap-3">
15+
<NavItem href="/sustainability">Sustainability</NavItem>
16+
<NavItem href="/process">Process</NavItem>
17+
<NavItem href="/about">About</NavItem>
18+
<NavItem href="/menu">Menu</NavItem>
19+
</nav>
20+
</nav>
21+
);
22+
}
23+
24+
type NavItemProps = {
25+
href: string;
26+
children: ComponentChildren;
27+
};
28+
29+
export function NavItem(props: NavItemProps): JSX.Element {
30+
return (
31+
<a
32+
class="rounded-xl p-1 font-semibold text-lg transition-all hover:underline aria-[current]:underline"
33+
href={props.href}
34+
>
35+
{props.children}
36+
</a>
37+
);
38+
}

components/Loading.tsx

+9
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
import type { JSX } from "preact";
2+
3+
export function Loading(): JSX.Element {
4+
return (
5+
<div class="grid w-24 place-items-center">
6+
<div class="loader" />
7+
</div>
8+
);
9+
}

components/Split.tsx

+47
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
import type { ComponentChildren, JSX } from "preact";
2+
import { Image } from "../islands/Image.tsx";
3+
4+
interface SplitProps {
5+
left: ComponentChildren;
6+
right: ComponentChildren;
7+
}
8+
9+
export function Split({ left, right }: SplitProps): JSX.Element {
10+
return (
11+
<div class="flex w-full max-w-6xl flex-col items-center justify-center overflow-hidden rounded-2xl bg-green-400 md:max-h-72 md:flex-row dark:bg-green-700">
12+
<div class="w-full md:w-7/12">{left}</div>
13+
14+
<div class="w-full md:w-5/12">{right}</div>
15+
</div>
16+
);
17+
}
18+
19+
interface SplitTextItemProps {
20+
children: ComponentChildren;
21+
}
22+
23+
export function SplitTextItem({ children }: SplitTextItemProps): JSX.Element {
24+
return (
25+
<div class="prose prose-slate dark:prose-invert place-items-center p-8 text-lg [&_h2]:text-center">
26+
{children}
27+
</div>
28+
);
29+
}
30+
31+
export interface SplitImageItemProps {
32+
id: string;
33+
34+
nofocus?: boolean;
35+
}
36+
37+
export function SplitImageItem(props: SplitImageItemProps): JSX.Element {
38+
return (
39+
<Image
40+
height={288}
41+
width={650}
42+
class="h-72 w-full object-cover"
43+
description=""
44+
{...props}
45+
/>
46+
);
47+
}

components/styles.ts

+18
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
import { tw } from "../utils/tags.ts";
2+
3+
/**
4+
* A nice round button.
5+
*/
6+
export const floatingButtonStyles = tw`flex size-14 flex-row items-center justify-center rounded-full focus-visible:outline-none`;
7+
8+
/**
9+
* Rounded blue button styles.
10+
*
11+
* @see {@link floatingButtonStyles}
12+
*/
13+
export const greenButtonStyles = tw`bg-green-400 shadow-md dark:bg-green-800 focus-visible:ring-1 focus-visible:ring-offset-2`;
14+
15+
/**
16+
* Make focus rings tolerable.
17+
*/
18+
export const prettyFocus = tw`focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-slate-50/70`;

deno.json

+55-5
Original file line numberDiff line numberDiff line change
@@ -15,10 +15,10 @@
1515
"dependencies": ["lint:deno", "ci:biome", "typecheck"]
1616
},
1717
"ci:biome": "biome ci --error-on-warnings",
18-
"typecheck": "deno check -q **/*.ts **/*.tsx",
19-
"dev": "deno run -A --watch=static/,routes/ dev.ts",
20-
"build": "deno run -A dev.ts build",
21-
"start": "deno run -A main.ts",
18+
"typecheck": "deno check -q .",
19+
"dev": "deno run -A --env-file --watch=static/,routes/ dev.ts",
20+
"build": "deno run -A --env-file dev.ts build",
21+
"start": "deno run -A --env-file main.ts",
2222
"update": "deno run -A -r jsr:@fresh/update ."
2323
},
2424
"lint": {
@@ -45,10 +45,60 @@
4545
"@cloudinary/html": "./vendor/@cloudinary/html/index.ts",
4646
"@cloudinary/url-gen": "npm:@cloudinary/url-gen@^1.21.0",
4747
"@fresh/plugin-tailwind": "jsr:@fresh/plugin-tailwind@^0.0.1-alpha.7",
48+
"@headlessui/react": "https://esm.sh/*@headlessui/[email protected]",
49+
"@openai/openai": "jsr:@openai/openai@^4.87.4",
4850
"@preact/signals": "npm:@preact/signals@^1.3.2",
51+
"@tabler/icons-preact": "npm:@tabler/icons-preact@^3.31.0",
52+
"@tailwindcss/forms": "npm:@tailwindcss/forms@^0.5.10",
53+
"@tailwindcss/typography": "npm:@tailwindcss/typography@^0.5.16",
54+
"@types/hast": "npm:@types/hast@^3.0.4",
55+
"clsx": "npm:clsx@^2.1.1",
56+
"effect": "npm:effect@^3.13.12",
4957
"fresh": "jsr:@fresh/core@^2.0.0-alpha.29",
58+
"idb-keyval": "npm:idb-keyval@^6.2.1",
5059
"preact": "npm:preact@^10.26.2",
51-
"tailwindcss": "npm:tailwindcss@^3.4.17"
60+
"rehype-raw": "npm:rehype-raw@^7.0.0",
61+
"rehype-react": "npm:rehype-react@^8.0.0",
62+
"rehype-sanitize": "npm:rehype-sanitize@^6.0.0",
63+
"remark-gfm": "npm:remark-gfm@^4.0.1",
64+
"remark-parse": "npm:remark-parse@^11.0.0",
65+
"remark-rehype": "npm:remark-rehype@^11.1.1",
66+
"tailwind-scrollbar": "npm:tailwind-scrollbar@^3.1.0",
67+
"tailwindcss": "npm:tailwindcss@^3.4.17",
68+
"unified": "npm:unified@^11.0.5"
69+
},
70+
"scopes": {
71+
"https://esm.sh": {
72+
"@floating-ui/core": "npm:@floating-ui/[email protected]",
73+
"@floating-ui/dom": "npm:@floating-ui/[email protected]",
74+
"@floating-ui/react": "https://esm.sh/*@floating-ui/[email protected]?target=esnext",
75+
"@floating-ui/react/": "https://esm.sh/*@floating-ui/[email protected]&target=esnext/",
76+
"@floating-ui/react-dom": "https://esm.sh/*@floating-ui/[email protected]?target=esnext",
77+
"@floating-ui/utils": "npm:@floating-ui/[email protected]",
78+
"@react-aria/focus": "https://esm.sh/*@react-aria/[email protected]?target=esnext",
79+
"@react-aria/interactions": "https://esm.sh/*@react-aria/[email protected]?target=esnext",
80+
"@react-aria/ssr": "https://esm.sh/*@react-aria/[email protected]?target=esnext",
81+
"@react-aria/utils": "https://esm.sh/*@react-aria/[email protected]?target=esnext",
82+
"@react-stately/flags": "https://esm.sh/*@react-stately/[email protected]?target=esnext",
83+
"@react-stately/utils": "https://esm.sh/*@react-stately/[email protected]?target=esnext",
84+
"@react-types/shared": "npm:@react-types/[email protected]",
85+
"@swc/helpers": "npm:@swc/[email protected]",
86+
"@tanstack/react-virtual": "https://esm.sh/*@tanstack/[email protected]?target=esnext",
87+
"@tanstack/virtual-core": "npm:@tanstack/[email protected]",
88+
"@types/react": "npm:[email protected]/compat",
89+
"react-dom": "npm:[email protected]/compat",
90+
"react": "npm:[email protected]/compat",
91+
"react/jsx-runtime": "npm:[email protected]/jsx-runtime",
92+
"react-dom/test-utils": "npm:[email protected]/test-utils",
93+
"tabbable": "npm:[email protected]",
94+
95+
"node:process": "https://esm.sh/node/process.mjs",
96+
"https://esm.sh/@types/react@~18.3.18/index.d.ts": "npm:[email protected]/compat",
97+
"https://esm.sh/@types/react@~19.0.10/index.d.ts": "npm:[email protected]/compat",
98+
"https://esm.sh/@types/react@~19.0.11/index.d.ts": "npm:[email protected]/compat",
99+
"https://esm.sh/@types/react@~19.0.7/index.d.ts": "npm:[email protected]/compat",
100+
"https://esm.sh/@types/react@~19.0.8/index.d.ts": "npm:[email protected]/compat"
101+
}
52102
},
53103
"compilerOptions": {
54104
"lib": ["dom", "dom.asynciterable", "dom.iterable", "deno.ns"],

0 commit comments

Comments
 (0)