Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .vscode/settings.json
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@
"tailwindCSS.classFunctions": ["cn"],
"tailwindCSS.experimental.configFile": {
"apps/admin/src/styles/main.css": "apps/admin/**",
"apps/website/tailwind.config.ts": "apps/website/**",
"apps/website/src/styles/main.css": "apps/website/**",
"apps/show/src/styles/main.css": "apps/show/**",
"libs/tailwindcss-peekaboo/storybook/styles.css": "libs/tailwindcss-peekaboo/**"
},
Expand Down
1 change: 0 additions & 1 deletion apps/admin/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,6 @@ COPY libs/password/package.json libs/password/
COPY libs/prisma/package.json libs/prisma/
COPY libs/react-primitives/package.json libs/react-primitives/
COPY libs/search-params-io/package.json libs/search-params-io/
COPY libs/tailwind-animation/package.json libs/tailwind-animation/
COPY libs/tailwindcss-peekaboo/package.json libs/tailwindcss-peekaboo/
COPY libs/zod-utils/package.json libs/zod-utils/
COPY scripts/package.json scripts/
Expand Down
1 change: 0 additions & 1 deletion apps/show/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,6 @@ COPY libs/password/package.json libs/password/
COPY libs/prisma/package.json libs/prisma/
COPY libs/react-primitives/package.json libs/react-primitives/
COPY libs/search-params-io/package.json libs/search-params-io/
COPY libs/tailwind-animation/package.json libs/tailwind-animation/
COPY libs/tailwindcss-peekaboo/package.json libs/tailwindcss-peekaboo/
COPY libs/zod-utils/package.json libs/zod-utils/
COPY scripts/package.json scripts/
Expand Down
1 change: 0 additions & 1 deletion apps/website/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,6 @@ COPY libs/password/package.json libs/password/
COPY libs/prisma/package.json libs/prisma/
COPY libs/react-primitives/package.json libs/react-primitives/
COPY libs/search-params-io/package.json libs/search-params-io/
COPY libs/tailwind-animation/package.json libs/tailwind-animation/
COPY libs/tailwindcss-peekaboo/package.json libs/tailwindcss-peekaboo/
COPY libs/zod-utils/package.json libs/zod-utils/
COPY scripts/package.json scripts/
Expand Down
6 changes: 5 additions & 1 deletion apps/website/eslint.config.js
Original file line number Diff line number Diff line change
@@ -1 +1,5 @@
export { default } from "@animeaux/dev-tools/eslint"
import { createConfig } from "@animeaux/dev-tools/eslint"

export default createConfig({
tailwindStylesheet: "./src/styles/main.css",
})
9 changes: 6 additions & 3 deletions apps/website/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -39,10 +39,10 @@
"@remix-run/react": "catalog:",
"@remix-run/serve": "catalog:",
"body-scroll-lock": "catalog:",
"es-toolkit": "catalog:",
"focus-trap": "catalog:",
"history": "catalog:",
"isbot": "catalog:",
"es-toolkit": "catalog:",
"luxon": "catalog:",
"react": "catalog:",
"react-dom": "catalog:",
Expand All @@ -57,22 +57,25 @@
"devDependencies": {
"@animeaux/dev-tools": "workspace:*",
"@remix-run/dev": "catalog:",
"@tailwindcss/node": "catalog:tailwindcss4",
"@tailwindcss/vite": "catalog:tailwindcss4",
"@types/body-scroll-lock": "catalog:",
"@types/culori": "catalog:",
"@types/luxon": "catalog:",
"@types/node": "catalog:",
"@types/react": "catalog:",
"@types/react-dom": "catalog:",
"@types/react-transition-group": "catalog:",
"autoprefixer": "catalog:",
"chokidar": "catalog:",
"culori": "catalog:",
"dotenv-flow": "catalog:",
"eslint": "catalog:",
"msw": "catalog:",
"node-html-parser": "catalog:",
"npm-run-all": "catalog:",
"prettier": "catalog:",
"prettier-plugin-tailwindcss": "catalog:",
"tailwindcss": "catalog:",
"tailwindcss": "catalog:tailwindcss4",
"tsx": "catalog:",
"typescript": "catalog:",
"vite": "catalog:",
Expand Down
7 changes: 0 additions & 7 deletions apps/website/postcss.config.js

This file was deleted.

6 changes: 1 addition & 5 deletions apps/website/prettier.config.js
Original file line number Diff line number Diff line change
@@ -1,5 +1 @@
import { createConfig } from "@animeaux/dev-tools/prettier"

export default createConfig({
tailwindConfig: "./tailwind.config.ts",
})
export { default } from "@animeaux/dev-tools/prettier"
125 changes: 117 additions & 8 deletions apps/website/scripts/generate-theme.ts
Original file line number Diff line number Diff line change
@@ -1,28 +1,42 @@
import { mkdir, writeFile } from "node:fs/promises"
import { mkdir, readFile, writeFile } from "node:fs/promises"
import { dirname, resolve } from "node:path"
import { fileURLToPath } from "node:url"

import { colors, screens } from "../tailwind.config.js"
import { __unstable__loadDesignSystem as loadDesignSystem } from "@tailwindcss/node"
import { formatHex } from "culori"
import { orderBy } from "es-toolkit"
import type { __unstable__loadDesignSystem as loadDesignSystemBase } from "tailwindcss"
import invariant from "tiny-invariant"

import { relativeToCwd, safelyReadFile } from "./shared.js"

const FILENAME = fileURLToPath(import.meta.url)
const DIRNAME = dirname(FILENAME)
const SCRIPT_NAME = relativeToCwd(FILENAME)
const STYLES_DIRECTORY = resolve(DIRNAME, "../src/styles")
const MAIN_STYLE_FILE = resolve(STYLES_DIRECTORY, "main.css")
const DEST_DIRECTORY = resolve(DIRNAME, "../src/generated")
const DEST_FILE = resolve(DEST_DIRECTORY, "theme.ts")

console.log("Building...")

const mainCss = await readFile(MAIN_STYLE_FILE, "utf-8")

// The type cast is required because @tailwindcss/node's `loadDesignSystem`
// types are broken (`design.theme: any`). We use tailwindcss's
// `loadDesignSystem` types because it's used internally and its types are fine.
const design = (await loadDesignSystem(mainCss, {
base: STYLES_DIRECTORY,
})) as Awaited<ReturnType<typeof loadDesignSystemBase>>

// Ensure the destination directory exists.
await mkdir(DEST_DIRECTORY, { recursive: true })

// Add here values we need in code.
// Only add what is actualy needed to keep a minimal size.
const theme = { screens, colors }

const content = `// This file is generated by ${SCRIPT_NAME}
export type ScreenSize = keyof typeof theme.screens
export const theme = ${JSON.stringify(theme, null, 2)}`
${getBreakpoints()}

${getColors()}
`

const currentContent = await safelyReadFile(DEST_FILE)
if (currentContent === content) {
Expand All @@ -35,3 +49,98 @@ if (currentContent === content) {

await writeFile(DEST_FILE, content)
console.info(`Built theme (${relativeToCwd(DEST_FILE)})`)

function getBreakpoints() {
const breakpoints = design.theme.namespace("--breakpoint")

const sortedEntries = orderBy(
Array.from(breakpoints.entries())
// `null` is used as key for a property with the namespace's name (e.g.
// `--breakpoint: <value>`).
// For breakpoints this shouldn't happen.
.filter((entry): entry is [string, string] => entry[0] != null)
.map(
([breakpoint, length]) =>
[breakpoint, getLengthValuePx(length)] as const,
),
[([_breakpoint, valuePx]) => valuePx],
["desc"],
)

return `export type Breakpoint = typeof Breakpoint.names[number]

export namespace Breakpoint {
/** Breakpoint names sorted by descending value. */
export const names = ${JSON.stringify(
sortedEntries.map(([breakpoint]) => breakpoint),
)} as const

/** Breakpoint values in pixels. */
export const value = ${JSON.stringify(Object.fromEntries(sortedEntries), null, 2)} as const
}`
}

function getColors() {
const colors = design.theme.namespace("--color")

const brandBlue = colors.get("brand-blue-800")
const gray50 = colors.get("gray-50")

return `export namespace Color {
export const brandBlue = ${JSON.stringify(brandBlue)}
export const brandBlueHex = ${JSON.stringify(formatHex(brandBlue))}
export const gray50 = ${JSON.stringify(gray50)}
export const gray50Hex = ${JSON.stringify(formatHex(gray50))}

export const background = Color.gray50
export const backgroundHex = Color.gray50Hex
export const primary = Color.brandBlue
export const primaryHex = Color.brandBlueHex
}`
}

/**
* Returns the given CSS length in pixels.
*
* @param length expressed with the `<length>` CSS type.
* @returns The value in pixels
* @see https://developer.mozilla.org/en-US/docs/Web/CSS/Reference/Values/length
*/
function getLengthValuePx(length: string) {
length = length.trim()

/**
* Matches values for the `<length>` CSS type.
* We only support unitless `0`, rem and px.
*
* @see https://developer.mozilla.org/en-US/docs/Web/CSS/Reference/Values/length
*/
const CSS_LENGTH_REGEXP = /(?<value>-?\d*\.?\d+)(?<unit>.*)/

const match = length.match(CSS_LENGTH_REGEXP)

invariant(
match?.groups?.value != null,
`Invalid CSS length from theme: "${length}"`,
)

const value = Number(match.groups.value)

// The unit is optional when the value is `0`.
// See https://developer.mozilla.org/en-US/docs/Web/CSS/Reference/Values/length#syntax
const unit = match.groups.unit || "px"

switch (unit) {
case "px": {
return value
}

case "rem": {
return 16 * value
}

default: {
throw new Error(`Unsupported length unit: "${unit}" in "${length}"`)
}
}
}
21 changes: 9 additions & 12 deletions apps/website/src/animals/item.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -55,31 +55,28 @@ export function AnimalItem({
<div className="flex flex-col">
<p className="flex items-start gap-1">
<span
className={cn("flex h-6 flex-none items-center text-[20px]", {
className={cn("flex h-6 flex-none items-center icon-20", {
"text-pink-500": animal.gender === Gender.FEMALE,
"text-brandBlue": animal.gender === Gender.MALE,
"text-brand-blue": animal.gender === Gender.MALE,
})}
title={GENDER_TRANSLATION[animal.gender]}
>
<Icon id={animal.gender === Gender.FEMALE ? "venus" : "mars"} />
</span>

<span
className={cn(
"flex-1 transition-colors duration-100 ease-in-out text-title-item",
{
"group-hover:text-brandBlue":
!isDisabled && animal.gender === Gender.MALE,
"group-hover:text-pink-500":
!isDisabled && animal.gender === Gender.FEMALE,
},
)}
className={cn("flex-1 text-item-title transition-colors", {
"group-hover:text-brand-blue":
!isDisabled && animal.gender === Gender.MALE,
"group-hover:text-pink-500":
!isDisabled && animal.gender === Gender.FEMALE,
})}
>
{animal.name}
</span>
</p>

<p className="flex items-start gap-6 text-gray-500 text-caption-default">
<p className="flex items-start gap-6 text-caption text-gray-500">
<span className="flex-1">{speciesLabels}</span>
<span className="flex-none">{formatAge(animal.birthdate)}</span>
</p>
Expand Down
4 changes: 2 additions & 2 deletions apps/website/src/blog/item.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -30,14 +30,14 @@ export function ArticleItem({
/>

<div className="flex flex-col">
<p className="text-gray-500 text-caption-default">
<p className="text-caption text-gray-500">
{DateTime.fromISO(article.publicationDate).toLocaleString(
DateTime.DATE_MED,
)}{" "}
par {article.authorName}
</p>

<p className="transition-colors duration-100 ease-in-out text-title-item group-hover:text-brandBlue">
<p className="text-item-title transition-colors group-hover:text-brand-blue">
{article.title}
</p>
<p>{article.description}</p>
Expand Down
10 changes: 5 additions & 5 deletions apps/website/src/core/actions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,17 +3,17 @@ import { cn } from "@animeaux/core"
type ActionColor = "blue" | "yellow" | "gray"

const STANDALONE_ACTION_COLOR_CLASS_NAME: Record<ActionColor, string> = {
blue: "bg-brandBlue text-white hover:bg-brandBlue-lighter",
yellow: "bg-brandYellow text-black hover:bg-brandYellow-darker",
gray: "bg-gray-100 hover:bg-gray-200",
blue: cn("bg-brand-blue text-white hover:bg-brand-blue-lighter"),
yellow: cn("bg-brand-yellow text-black hover:bg-brand-yellow-darker"),
gray: cn("bg-gray-100 hover:bg-gray-200"),
}

export const actionClassNames = {
standalone: ({ color = "blue" }: { color?: ActionColor } = {}) =>
cn(
"flex min-w-max flex-none items-center justify-center px-6 py-2 transition-[background-color,transform] duration-100 ease-in-out rounded-bubble-sm text-body-emphasis active:scale-95",
"flex min-w-max flex-none items-center justify-center rounded-bubble-sm px-6 py-2 text-body-emphasis transition-[background-color,scale] active:scale-95",
STANDALONE_ACTION_COLOR_CLASS_NAME[color],
),
proseInline: () =>
"border-b text-body-emphasis border-b-brandBlue hover:border-b-2",
cn("border-b border-b-brand-blue text-body-emphasis hover:border-b-2"),
}
4 changes: 2 additions & 2 deletions apps/website/src/core/controllers/paginator.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -77,9 +77,9 @@ function PaginatorItem({
{
"w-[40px] rounded-bubble-sm": !isEllipsis,
},
"flex items-center justify-center transition-colors duration-100 ease-in-out text-body-emphasis",
"flex items-center justify-center text-body-emphasis transition-colors",
{
"bg-brandBlue text-white": isActive,
"bg-brand-blue text-white": isActive,
"hover:bg-gray-200": !isActive && to != null,
},
)}
Expand Down
6 changes: 3 additions & 3 deletions apps/website/src/core/controllers/searchForm.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,7 @@ export function SearchForm({
<div
className={cn(
className,
"flex gap-2 rounded-bl-[10px] rounded-br-[16px] rounded-tl-[16px] rounded-tr-[10px] bg-white p-1 shadow-base",
"flex gap-2 rounded-tl-[16px] rounded-tr-[10px] rounded-br-[16px] rounded-bl-[10px] bg-white p-1 shadow-base",
)}
>
<Select<AnimalSpeciesOption>
Expand Down Expand Up @@ -135,7 +135,7 @@ export function SearchForm({
<BaseLink
to={getPath(state)}
title="Rechercher"
className="flex flex-none bg-brandBlue p-3 text-white transition-[background-color,transform] duration-100 ease-in-out rounded-bubble-sm active:scale-95 hover:bg-brandBlue-lighter"
className="flex flex-none rounded-bubble-sm bg-brand-blue p-3 text-white transition-[background-color,scale] hover:bg-brand-blue-lighter active:scale-95"
>
<Icon id="magnifying-glass" />
</BaseLink>
Expand All @@ -161,7 +161,7 @@ function Select<ValueType extends string>({
onChange(event.target.value as ValueType)
}}
className={cn(
"min-w-0 flex-1 cursor-pointer appearance-none bg-transparent px-6 py-2 transition-colors duration-100 ease-in-out rounded-bubble-sm hover:bg-gray-50",
"min-w-0 flex-1 cursor-pointer appearance-none rounded-bubble-sm bg-transparent px-6 py-2 transition-colors hover:bg-gray-50",
{ "text-gray-500": value == null },
)}
>
Expand Down
6 changes: 3 additions & 3 deletions apps/website/src/core/controllers/tabs.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -21,9 +21,9 @@ export function Tab({
type="button"
onClick={onSelect}
className={cn(
"relative flex px-3 py-2 transition-colors duration-150 ease-in-out text-body-emphasis",
"relative flex px-3 py-2 text-body-emphasis transition-colors duration-slow",
{
"text-brandBlue": isActive,
"text-brand-blue": isActive,
"text-gray-600 hover:text-black": !isActive,
},
)}
Expand All @@ -34,7 +34,7 @@ export function Tab({
{(transitionState) => (
<LineShapeHorizontal
className={cn("absolute bottom-0 left-0 block h-1 w-full", {
"transition-[stroke-dashoffset] duration-150 ease-in-out":
"transition-[stroke-dashoffset] duration-slow":
transitionState === "entering" || transitionState === "exiting",
})}
style={{
Expand Down
Loading
Loading