diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json index e298a1ba..bc720ea2 100644 --- a/.devcontainer/devcontainer.json +++ b/.devcontainer/devcontainer.json @@ -16,15 +16,18 @@ "git.fetchOnPull": true, "git.rebaseWhenSync": true, "telemetry.telemetryLevel": "off", + "javascript.updateImportsOnFileMove.enabled": "always", "typescript.preferences.importModuleSpecifierEnding": "minimal", - "typescript.tsdk": "node_modules/typescript/lib" + "typescript.surveys.enabled": false, + "typescript.tsdk": "node_modules/typescript/lib", + "typescript.tsserver.log": "off", + "typescript.updateImportsOnFileMove.enabled": "always" }, "extensions": [ + "bradlc.vscode-tailwindcss", "dbaeumer.vscode-eslint", - "EditorConfig.EditorConfig", - "unifiedjs.vscode-mdx", "esbenp.prettier-vscode", - "stylelint.vscode-stylelint" + "unifiedjs.vscode-mdx" ] } }, diff --git a/.npmrc b/.npmrc index 6f07069f..d93a75a6 100644 --- a/.npmrc +++ b/.npmrc @@ -1,3 +1,2 @@ public-hoist-pattern[]=*eslint* public-hoist-pattern[]=*prettier* -public-hoist-pattern[]=*stylelint* diff --git a/.prettierrc.mjs b/.prettierrc.mjs index 49b1fa75..1be2c63a 100644 --- a/.prettierrc.mjs +++ b/.prettierrc.mjs @@ -1,12 +1,13 @@ /** @type {import("prettier").Config} */ const config = { - singleQuote: false, + plugins: ["prettier-plugin-tailwindcss"], jsxSingleQuote: false, printWidth: 120, - tabWidth: 2, - useTabs: false, quoteProps: "as-needed", + singleQuote: false, + tabWidth: 2, trailingComma: "es5", + useTabs: false, }; export default config; diff --git a/.stylelintrc.mjs b/.stylelintrc.mjs deleted file mode 100644 index dd849dc0..00000000 --- a/.stylelintrc.mjs +++ /dev/null @@ -1,19 +0,0 @@ -/* eslint-disable import/no-anonymous-default-export */ - -/** @type {import("stylelint").Config} */ -export default { - extends: ["stylelint-config-standard", "stylelint-config-css-modules", "stylelint-prettier/recommended"], - rules: { - "selector-class-pattern": null, - "custom-property-pattern": null, - "media-feature-range-notation": null, - "rule-empty-line-before": [ - "always-multi-line", - { - except: ["after-single-line-comment"], - ignore: ["inside-block"], - }, - ], - "color-hex-length": "long", - }, -}; diff --git a/.vscode/extensions.json b/.vscode/extensions.json new file mode 100644 index 00000000..2229ccd0 --- /dev/null +++ b/.vscode/extensions.json @@ -0,0 +1,8 @@ +{ + "recommendations": [ + "bradlc.vscode-tailwindcss", + "dbaeumer.vscode-eslint", + "esbenp.prettier-vscode", + "unifiedjs.vscode-mdx" + ] +} diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 00000000..fe47dfc4 --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,16 @@ +{ + "editor.tabSize": 2, + "editor.rulers": [ + 120 + ], + "files.associations": { + "*.css": "tailwindcss", + "*.mdx": "markdown" + }, + "javascript.updateImportsOnFileMove.enabled": "always", + "typescript.preferences.importModuleSpecifierEnding": "minimal", + "typescript.surveys.enabled": false, + "typescript.tsdk": "node_modules/typescript/lib", + "typescript.tsserver.log": "off", + "typescript.updateImportsOnFileMove.enabled": "always" +} diff --git a/app/contact/form.module.css b/app/contact/form.module.css index 743e9db3..0821bae8 100644 --- a/app/contact/form.module.css +++ b/app/contact/form.module.css @@ -2,15 +2,15 @@ width: 100%; padding: 0.8em; margin: 0.6em 0; - border: 2px solid var(--colors-light); + border: 2px solid var(--color-gray-400); border-radius: 0.6em; color: var(--colors-text); - background-color: var(--colors-super-duper-light); + background-color: var(--color-gray-100); } .input:focus { outline: none; - border-color: var(--colors-link); + border-color: var(--color-link); } .input.textarea { @@ -21,12 +21,12 @@ } .input.invalid { - border-color: var(--colors-error); + border-color: var(--color-error); } .errorMessage { font-size: 0.9em; - color: var(--colors-error); + color: var(--color-error); } .actionRow { @@ -48,13 +48,13 @@ user-select: none; font-weight: 500; color: var(--colors-text); - background-color: var(--colors-kinda-light); + background-color: var(--color-gray-300); } .submitButton:hover, .submitButton:focus-visible { - color: var(--colors-super-duper-light); - background-color: var(--colors-link); + color: var(--color-gray-100); + background-color: var(--color-link); } .submitIcon { @@ -70,11 +70,11 @@ } .result.success { - color: var(--colors-success); + color: var(--color-success); } .result.error { - color: var(--colors-error); + color: var(--color-error); } .resultIcon { diff --git a/app/contact/form.tsx b/app/contact/form.tsx index 28ed5910..7cab7590 100644 --- a/app/contact/form.tsx +++ b/app/contact/form.tsx @@ -97,7 +97,7 @@ const ContactForm = () => { Markdown syntax {" "} is allowed here, e.g.: **bold**, _italics_, [ - + links ](https://jarv.is), and `code`. diff --git a/app/contact/page.tsx b/app/contact/page.tsx index 1e03f0fd..e6803490 100644 --- a/app/contact/page.tsx +++ b/app/contact/page.tsx @@ -32,7 +32,7 @@ const Page = () => { size="0.975em" style={{ marginRight: "0.15em", - stroke: "var(--colors-warning)", + stroke: "var(--color-warning)", verticalAlign: "middle", }} />{" "} diff --git a/app/fonts.ts b/app/fonts.ts index 548cac31..2ef1555a 100644 --- a/app/fonts.ts +++ b/app/fonts.ts @@ -11,7 +11,7 @@ export const GeistSans = GeistSansLoader({ "system-ui", "sans-serif", ], - variable: "--fonts-sans", + variable: "--font-geist-sans", preload: true, }); @@ -29,6 +29,6 @@ export const GeistMono = GeistMonoLoader({ "monospace", ], adjustFontFallback: false, - variable: "--fonts-mono", + variable: "--font-geist-mono", preload: true, }); diff --git a/app/globals.css b/app/globals.css index b1046191..b3429bb9 100644 --- a/app/globals.css +++ b/app/globals.css @@ -1,69 +1,98 @@ -/*! - * modern-normalize v3.0.1 | MIT License | https://github.com/sindresorhus/modern-normalize/tree/v3.0.1 - */ +@import "tailwindcss"; -*, -::before, -::after { - box-sizing: border-box; +@theme inline { + --font-sans: var(--font-geist-sans); + --font-mono: var(--font-geist-mono); + + --container-default: var(--container-4xl); } -html { - line-height: 1.15; - tab-size: 4; - /* stylelint-disable-next-line property-no-vendor-prefix */ - -webkit-text-size-adjust: 100%; +@theme { + --color-*: initial; + --color-background-inner: oklch(1 0 0); + --color-background-outer: oklch(0.99 0 0); + --color-gray-900: oklch(0.23 0 0); + --color-gray-800: oklch(0.32 0 0); + --color-gray-700: oklch(0.43 0 0); + --color-gray-600: oklch(0.48 0 0); + --color-gray-500: oklch(0.56 0 0); + --color-gray-400: oklch(0.66 0 0); + --color-gray-300: oklch(0.78 0 0); + --color-gray-200: oklch(0.85 0 0); + --color-gray-100: oklch(0.99 0 0); + --color-link: oklch(0.53 0.1547 252.33); + --color-success: oklch(0.63 0.1557 144.2); + --color-warning: oklch(0.72 0.177693 55.7508); + --color-error: oklch(0.64 0.2505 28.39); + + --animate-wave: wave 5s ease 1s infinite; + --animate-heartbeat: heartbeat 10s ease 7.5s infinite; + + @keyframes wave { + 0% { + transform: rotate(0deg); + } + 5% { + transform: rotate(14deg); + } + 10% { + transform: rotate(-8deg); + } + 15% { + transform: rotate(14deg); + } + 20% { + transform: rotate(-4deg); + } + 25% { + transform: rotate(10deg); + } + 30% { + transform: rotate(0deg); + } + 100% { + transform: rotate(0deg); + } + } + + @keyframes heartbeat { + 0% { + transform: scale(1); + } + 2% { + transform: scale(1.25); + } + 4% { + transform: scale(1); + } + 6% { + transform: scale(1.2); + } + 8% { + transform: scale(1); + } + 100% { + transform: scale(1); + } + } } -body { - margin: 0; - font-family: var(--fonts-sans); - background-color: var(--colors-background-inner); -} +@custom-variant dark (&:where([data-theme=dark], [data-theme=dark] *)); -code, -kbd, -samp, -pre { - font-size: 1em; - font-family: var(--fonts-mono); - font-variant-ligatures: none; /* i hate them. fwiw. */ -} - -small { - font-size: 80%; -} - -sub, -sup { - font-size: 75%; - line-height: 0; - position: relative; - vertical-align: baseline; -} - -sub { - bottom: -0.25em; -} - -sup { - top: -0.5em; -} - -button, -input, -select, -textarea { - font-family: inherit; - font-size: 100%; - line-height: 1.15; - margin: 0; -} - -button, -[type="button"], -[type="reset"], -[type="submit"] { - /* stylelint-disable-next-line property-no-vendor-prefix */ - -webkit-appearance: button; +&:where([data-theme=dark], [data-theme=dark] *) { + --color-background-inner: oklch(0.24 0 0); + --color-background-outer: oklch(0.26 0 0); + --color-gray-900: oklch(0.93 0 0); + --color-gray-800: oklch(0.90 0 0); + --color-gray-700: oklch(0.88 0 0); + --color-gray-600: oklch(0.76 0 0); + --color-gray-500: oklch(0.67 0 0); + --color-gray-400: oklch(0.5 0 0); + --color-gray-300: oklch(0.44 0 0); + --color-gray-200: oklch(0.35 0 0); + --color-gray-100: oklch(0.24 0 0); + --color-link: oklch(0.81 0.1026 246.31); + --color-success: oklch(0.81 0.2001 138.65); + --color-warning: oklch(0.81 0.166 85.03); + --color-error: oklch(0.68 0.2105 24.73) } diff --git a/app/hillary/page.tsx b/app/hillary/page.tsx index 8c20e236..4c4b41a1 100644 --- a/app/hillary/page.tsx +++ b/app/hillary/page.tsx @@ -57,7 +57,7 @@ const Page = () => { fontSize: "0.9em", lineHeight: 1.8, margin: "1.25em 1em 0 1em", - color: "var(--colors-medium-light)", + color: "var(--color-gray-500)", }} > Video is property of{" "} diff --git a/app/layout.module.css b/app/layout.module.css deleted file mode 100644 index acb47ca0..00000000 --- a/app/layout.module.css +++ /dev/null @@ -1,26 +0,0 @@ -.layout { - display: flex; - flex-direction: column; - min-height: 100vh; -} - -.default { - width: 100%; - padding: 1.5em; - font-size: 0.9em; - line-height: 1.7; - color: var(--colors-text); -} - -.container { - width: 100%; - max-width: var(--max-width); - margin: 0 auto; -} - -@media (max-width: 768px) { - .default { - font-size: 0.925em; - line-height: 1.85; - } -} diff --git a/app/layout.tsx b/app/layout.tsx index 37bd982c..ca614d49 100644 --- a/app/layout.tsx +++ b/app/layout.tsx @@ -1,27 +1,27 @@ import { env } from "../lib/env"; import { JsonLd } from "react-schemaorg"; import { Analytics } from "@vercel/analytics/next"; -import clsx from "clsx"; import { ThemeProvider, ThemeScript } from "../contexts/ThemeContext"; import Header from "../components/Header"; import Footer from "../components/Footer"; import { SkipNavLink, SkipNavTarget } from "../components/SkipNav"; +import cn from "../lib/helpers/classnames"; import { defaultMetadata } from "../lib/helpers/metadata"; import * as config from "../lib/config"; -import { MAX_WIDTH } from "../lib/config/constants"; import type { Person, WebSite } from "schema-dts"; import { GeistMono, GeistSans } from "./fonts"; import "./globals.css"; -import "./themes.css"; - -import styles from "./layout.module.css"; export const metadata = defaultMetadata; const RootLayout = ({ children }: Readonly<{ children: React.ReactNode }>) => { return ( - + @@ -62,21 +62,16 @@ const RootLayout = ({ children }: Readonly<{ children: React.ReactNode }>) => { /> - + -
+
-
-
- - {children} -
+
+ +
{children}