Skip to content

Commit c5f00c3

Browse files
Upgrades (#5)
Upgrade package versions to latest, and upgrade some things related to new versions. - clean up prettier config, re-add `prettier-plugin-tailwindcss` just to format `.css` files. (more than just formatting, can catch CSS bugs like applying two conflicting classes) - remove `prettier-plugin-css-order` plugin, as it's barely necessary when 99% of styles are using tailwind - remove react-refresh eslint plugin. only rule was turned off anyway because warning was annoying, and rule generally isn't that helpful. - clean up eslint config to match current recommended style from each plugin library - format all files with prettier, not just src - use new v4 of eslint better tailwind plugin. prev version formatted classes like ``className={`classes`}``, new version just does `className="classes"`. - fix issue i noticed where database pill is not keyboard focusable - `@base-ui-components` moved out of beta and into v1, and became just `@base-ui` - make popover just use children prop instead of trigger, to be consistent with tooltip - move stuff that is common to tooltip/popover/autocomplete to tooltip (expected to be most commonly used) - manually provide aria label on tooltip target, as baseui doesn't - wrap global styles in `@layer components` for lower specificity and easier override in-situ (w/o need for `!`) - update `useChanged` hook and a useEffect to match new React Compiler Eslint rules
1 parent 30f968b commit c5f00c3

24 files changed

+561
-526
lines changed

frontend/.gitignore

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,3 +7,6 @@ dist
77
*.local
88

99
!.env
10+
11+
tsconfig.tsbuildinfo
12+
tsconfig.*.tsbuildinfo

frontend/.prettierrc

Lines changed: 29 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,31 @@
11
{
2-
"plugins": [
3-
"@ianvs/prettier-plugin-sort-imports",
4-
"./node_modules/prettier-plugin-jsdoc/dist/index.js",
5-
"prettier-plugin-css-order"
6-
],
7-
"importOrder": [
8-
"^react",
9-
"^@[a-zA-Z]",
10-
"^[a-zA-Z]",
11-
"^@/",
12-
"^/",
13-
"^./",
14-
"^../"
15-
],
16-
"importOrderParserPlugins": ["typescript", "jsx", "importAssertions"],
17-
"cssDeclarationSorterOrder": "smacss",
18-
"jsdocCapitalizeDescription": false,
19-
"htmlWhitespaceSensitivity": "strict"
2+
"overrides": [
3+
{
4+
"files": "*.css",
5+
"options": {
6+
"plugins": ["prettier-plugin-tailwindcss"]
7+
}
8+
},
9+
{
10+
"files": "*.*",
11+
"options": {
12+
"plugins": [
13+
"@ianvs/prettier-plugin-sort-imports",
14+
"prettier-plugin-jsdoc"
15+
],
16+
"importOrder": [
17+
"^react",
18+
"^@[a-zA-Z]",
19+
"^[a-zA-Z]",
20+
"^@/",
21+
"^/",
22+
"^./",
23+
"^../"
24+
],
25+
"importOrderParserPlugins": ["typescript", "jsx", "importAssertions"],
26+
"jsdocCapitalizeDescription": false,
27+
"htmlWhitespaceSensitivity": "strict"
28+
}
29+
}
30+
]
2031
}

frontend/bun.lock

Lines changed: 242 additions & 207 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

frontend/eslint.config.js

Lines changed: 26 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,50 +1,56 @@
11
import eslintJs from "@eslint/js";
22
import eslintPluginBetterTailwindcss from "eslint-plugin-better-tailwindcss";
33
import eslintPluginJsxA11y from "eslint-plugin-jsx-a11y";
4-
import eslintPluginPrettier from "eslint-plugin-prettier";
54
import eslintPluginPrettierRecommended from "eslint-plugin-prettier/recommended";
65
import eslintPluginReactHooks from "eslint-plugin-react-hooks";
7-
import eslintPluginReactRefresh from "eslint-plugin-react-refresh";
86
import { defineConfig, globalIgnores } from "eslint/config";
97
import globals from "globals";
108
import typescriptEslint from "typescript-eslint";
119

1210
export default defineConfig([
1311
globalIgnores(["dist", "public"]),
12+
eslintJs.configs.recommended,
13+
typescriptEslint.configs.recommended,
14+
eslintPluginPrettierRecommended,
15+
eslintPluginReactHooks.configs.flat.recommended,
16+
eslintPluginJsxA11y.flatConfigs.recommended,
1417
{
15-
files: ["**/*.{ts,tsx}"],
16-
extends: [
17-
eslintJs.configs.recommended,
18-
typescriptEslint.configs.recommended,
19-
eslintPluginReactHooks.configs["recommended-latest"],
20-
eslintPluginReactRefresh.configs.vite,
21-
eslintPluginPrettierRecommended,
22-
eslintPluginJsxA11y.flatConfigs.recommended,
23-
],
2418
plugins: {
25-
prettier: eslintPluginPrettier,
2619
"better-tailwindcss": eslintPluginBetterTailwindcss,
2720
},
2821
languageOptions: {
2922
ecmaVersion: 2020,
3023
globals: globals.browser,
3124
},
3225
rules: {
33-
...eslintPluginReactHooks.configs.recommended.rules,
34-
...eslintPluginBetterTailwindcss.configs["recommended-warn"].rules,
35-
"prettier/prettier": "warn",
26+
/** GENERAL */
3627
"prefer-const": ["error", { destructuring: "all" }],
28+
29+
/** TYPESCRIPT */
3730
"@typescript-eslint/no-unused-vars": ["warn", { caughtErrors: "none" }],
3831
"@typescript-eslint/consistent-type-definitions": ["error", "type"],
3932
"@typescript-eslint/consistent-type-imports": "error",
40-
"react-refresh/only-export-components": ["off"],
33+
34+
/** ACCESSIBILITY */
35+
/** https://github.com/dequelabs/axe-core/issues/4566 */
36+
"jsx-a11y/no-noninteractive-tabindex": ["off"],
37+
/**
38+
* allow <label>some text<AnyComponent/></label> but still catch
39+
* <label>just text</label>
40+
*/
4141
"jsx-a11y/label-has-associated-control": [
4242
"error",
43-
{ controlComponents: ["Select"] },
43+
{ controlComponents: ["*"] },
44+
],
45+
46+
/** FORMATTING */
47+
"prettier/prettier": "warn",
48+
...eslintPluginBetterTailwindcss.configs["recommended-warn"].rules,
49+
/** https://github.com/schoero/eslint-plugin-better-tailwindcss/issues/302 */
50+
"better-tailwindcss/enforce-consistent-line-wrapping": [
51+
"warn",
52+
{ strictness: "loose" },
4453
],
45-
"better-tailwindcss/enforce-consistent-variable-syntax": "warn",
46-
"better-tailwindcss/enforce-shorthand-classes": "warn",
47-
"better-tailwindcss/no-deprecated-classes": "warn",
4854
},
4955
settings: { "better-tailwindcss": { entryPoint: "src/styles.css" } },
5056
},

frontend/package.json

Lines changed: 31 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -5,62 +5,60 @@
55
"build": "vite build",
66
"preview": "vite preview",
77
"lint": "eslint . --fix",
8-
"format": "prettier --write src/",
8+
"format": "prettier --write .",
99
"test:types": "tsc -b",
1010
"test:lint": "eslint .",
11-
"test:format": "prettier --check src/",
11+
"test:format": "prettier --check .",
1212
"test": "bun run test:types && bun run test:lint && bun run test:format",
1313
"clean": "rm -rf node_modules dist bun.lock && bun pm cache rm"
1414
},
1515
"dependencies": {
16-
"@base-ui-components/react": "^1.0.0-beta.4",
17-
"@reactuses/core": "^6.1.1",
18-
"@tailwindcss/vite": "^4.1.16",
19-
"@tanstack/react-query": "^5.90.5",
16+
"@base-ui/react": "^1.1.0",
17+
"@reactuses/core": "^6.1.11",
18+
"@tailwindcss/vite": "^4.1.18",
19+
"@tanstack/react-query": "^5.90.19",
2020
"@tanstack/react-table": "^8.21.3",
2121
"clsx": "^2.1.1",
22-
"gsap": "^3.13.0",
22+
"gsap": "^3.14.2",
2323
"highlight.js": "^11.11.1",
2424
"javascript-time-ago": "^2.5.12",
25-
"jotai": "^2.15.0",
25+
"jotai": "^2.16.2",
2626
"lodash": "^4.17.21",
27-
"lucide-react": "^0.548.0",
28-
"react": "^19.2.0",
27+
"lucide-react": "^0.562.0",
28+
"react": "^19.2.3",
2929
"react-children-utilities": "^2.10.0",
30-
"react-dom": "^19.2.0",
31-
"react-router": "^7.9.4",
30+
"react-dom": "^19.2.3",
31+
"react-router": "^7.12.0",
3232
"react-time-ago": "^7.3.5",
33-
"tailwindcss": "^4.1.16"
33+
"tailwindcss": "^4.1.18"
3434
},
3535
"devDependencies": {
36-
"@eslint/js": "^9.38.0",
36+
"@eslint/js": "^9.39.2",
3737
"@ianvs/prettier-plugin-sort-imports": "^4.7.0",
38-
"@types/lodash": "^4.17.20",
39-
"@types/react": "^19.2.2",
40-
"@types/react-dom": "^19.2.2",
41-
"@vitejs/plugin-react": "^5.1.0",
42-
"eslint": "^9.38.0",
38+
"@types/lodash": "^4.17.23",
39+
"@types/react": "^19.2.9",
40+
"@types/react-dom": "^19.2.3",
41+
"@vitejs/plugin-react": "^5.1.2",
42+
"eslint": "^9.39.2",
4343
"eslint-config-prettier": "^10.1.8",
44-
"eslint-plugin-better-tailwindcss": "^3.7.11",
44+
"eslint-plugin-better-tailwindcss": "^4.0.1",
4545
"eslint-plugin-jsx-a11y": "^6.10.2",
46-
"eslint-plugin-prettier": "^5.5.4",
47-
"eslint-plugin-react-hooks": "^5.2.0",
48-
"eslint-plugin-react-refresh": "^0.4.24",
49-
"globals": "^16.4.0",
50-
"msw": "^2.11.6",
51-
"postcss": "^8.5.6",
52-
"prettier": "^3.6.2",
53-
"prettier-plugin-css-order": "^2.1.2",
54-
"prettier-plugin-jsdoc": "^1.3.3",
55-
"type-fest": "^5.1.0",
46+
"eslint-plugin-prettier": "^5.5.5",
47+
"eslint-plugin-react-hooks": "^7.0.1",
48+
"globals": "^17.0.0",
49+
"msw": "^2.12.7",
50+
"prettier": "^3.8.0",
51+
"prettier-plugin-jsdoc": "^1.8.0",
52+
"prettier-plugin-tailwindcss": "^0.7.2",
53+
"type-fest": "^5.4.1",
5654
"typescript": "^5.9.3",
57-
"typescript-eslint": "^8.46.2",
58-
"vite": "^7.1.12",
55+
"typescript-eslint": "^8.53.1",
56+
"vite": "^7.3.1",
5957
"vite-plugin-svgr": "^4.5.0"
6058
},
6159
"msw": {
6260
"workerDirectory": [
6361
"public"
6462
]
6563
}
66-
}
64+
}

frontend/public/mockServiceWorker.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
* - Please do NOT modify this file.
88
*/
99

10-
const PACKAGE_VERSION = '2.11.6'
10+
const PACKAGE_VERSION = '2.12.7'
1111
const INTEGRITY_CHECKSUM = '4db4a41e972cec1b64cc569c66952d82'
1212
const IS_MOCKED_RESPONSE = Symbol('isMockedResponse')
1313
const activeClientIds = new Set()

frontend/src/App.tsx

Lines changed: 9 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import { useEffect } from "react";
12
import {
23
createBrowserRouter,
34
Outlet,
@@ -15,8 +16,7 @@ import NotFound from "@/pages/NotFound";
1516
import Search from "@/pages/Search";
1617
import Testbed from "@/pages/Testbed";
1718
import { getDocBbox, glow, scrollTo } from "@/util/dom";
18-
import { useChanged } from "@/util/hooks";
19-
import { sleep, waitFor, waitForStable } from "@/util/misc";
19+
import { waitFor, waitForStable } from "@/util/misc";
2020
import { redirectPath, redirectState } from "@/util/url";
2121

2222
/** app entrypoint */
@@ -31,13 +31,11 @@ export default function App() {
3131
/** route layout */
3232
const Layout = () => {
3333
/** current route info */
34-
const { hash, pathname, search } = useLocation();
34+
const { hash } = useLocation();
3535

36-
/** which parts of route have changed from prev render */
37-
const hashChanged = useChanged(hash);
38-
const restChanged = useChanged({ pathname, search });
39-
40-
scrollToHash(hash, hashChanged, restChanged);
36+
useEffect(() => {
37+
scrollToHash(hash);
38+
}, [hash]);
4139

4240
return (
4341
<>
@@ -109,29 +107,13 @@ const queryClient = new QueryClient({
109107
});
110108

111109
/** scroll to target of url hash on page */
112-
const scrollToHash = async (
113-
hash: string,
114-
hashChanged: boolean,
115-
restChanged: boolean,
116-
) => {
117-
if (!hash) return;
118-
if (!hashChanged) return;
119-
110+
const scrollToHash = async (hash: string) => {
120111
/** wait for element to appear */
121112
const element = await waitFor(() => document.querySelector(hash));
122113
if (!element) return;
123114

124-
/**
125-
* if not just hash changed (indicating we may be on first load of page or
126-
* otherwise expecting significant layout changes)
127-
*/
128-
if (restChanged) {
129-
/** wait for layout shifts to stabilize */
130-
await waitForStable(() => getDocBbox(element).top);
131-
/** scroll down a bit to trigger small header */
132-
window.scrollBy(0, 100);
133-
await sleep();
134-
}
115+
/** wait for layout shifts to stabilize */
116+
await waitForStable(() => getDocBbox(element).top);
135117

136118
/** scroll to element */
137119
scrollTo(hash);

frontend/src/components/Autocomplete.tsx

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
11
import { useRef } from "react";
22
import type { ReactElement, ReactNode } from "react";
3-
import { Autocomplete as _Autocomplete } from "@base-ui-components/react/autocomplete";
3+
import { Autocomplete as _Autocomplete } from "@base-ui/react";
44
import clsx from "clsx";
55
import { Search } from "lucide-react";
6-
import { padding } from "@/components/Popover";
6+
import { padding } from "@/components/Tooltip";
77

88
type Props = {
99
/** search value */
@@ -45,7 +45,7 @@ export default function Autocomplete({
4545
mode="none"
4646
openOnInputClick
4747
>
48-
<div className={`relative flex items-center`}>
48+
<div className="relative flex items-center">
4949
<_Autocomplete.Input
5050
render={
5151
<input
@@ -61,19 +61,19 @@ export default function Autocomplete({
6161
/>
6262
}
6363
/>
64-
<Search className={`absolute right-0 px-2 text-theme`} />
64+
<Search className="absolute right-0 px-2 text-theme" />
6565
</div>
6666

6767
<_Autocomplete.Portal>
6868
<_Autocomplete.Positioner collisionPadding={padding}>
6969
<_Autocomplete.Popup
70-
className={`
70+
className="
7171
flex max-h-(--available-height) w-(--anchor-width) grow flex-col
7272
overflow-y-auto rounded-sm bg-white shadow-thick
73-
`}
73+
"
7474
>
7575
{status && (
76-
<_Autocomplete.Status className={`flex gap-2 p-2 leading-none`}>
76+
<_Autocomplete.Status className="flex gap-2 p-2 leading-none">
7777
{status}
7878
</_Autocomplete.Status>
7979
)}
@@ -83,10 +83,10 @@ export default function Autocomplete({
8383
<_Autocomplete.Item
8484
key={index}
8585
value={tag.value}
86-
className={`
86+
className="
8787
flex cursor-pointer gap-2 p-2 leading-none
8888
data-highlighted:bg-theme/10
89-
`}
89+
"
9090
onClick={(event) => {
9191
event.preventDefault();
9292
/** select option */

frontend/src/components/BigRadios.tsx

Lines changed: 4 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
import { useId } from "react";
22
import type { ReactElement, ReactNode } from "react";
3-
import { Radio } from "@base-ui-components/react/radio";
4-
import { RadioGroup } from "@base-ui-components/react/radio-group";
3+
import { Radio, RadioGroup } from "@base-ui/react";
54
import clsx from "clsx";
65
import { CheckCircle2 } from "lucide-react";
76

@@ -48,11 +47,11 @@ export default function BigRadios<O extends Option>({
4847
if (option.value === value) el?.scrollIntoView(true);
4948
}}
5049
key={index}
51-
className={`
50+
className="
5251
relative flex min-w-50 flex-1 cursor-pointer flex-col items-start!
5352
gap-2 rounded-sm border border-slate-300 p-2 transition-colors
5453
hover:bg-slate-100
55-
`}
54+
"
5655
>
5756
<Radio.Root
5857
value={option.value}
@@ -61,9 +60,7 @@ export default function BigRadios<O extends Option>({
6160
nativeButton={false}
6261
/>
6362
{option.value === value && (
64-
<CheckCircle2
65-
className={`absolute top-2 right-2 text-xl text-green-500`}
66-
/>
63+
<CheckCircle2 className="absolute top-2 right-2 text-xl text-green-500" />
6764
)}
6865
</label>
6966
))}

0 commit comments

Comments
 (0)