modern-normalize makes every element border-box, so the rules that set an explicit width or height and then added padding and a border on top came out short: headings lost the space under them, the horizontal rules went invisible and the item list icons narrowed. Restate each as the total it used to render as. Two rules are left alone on purpose: `.dbtools .index section` and `.databases` both say `width: 50%`, and widening them to `calc(50% + 20px)` drops the second float onto its own line. Also stop centring the h3 bullet on the padding box, which the bottom padding makes taller than the text -- it sat ~4.5px low both here and on the deployed site. Pin it to the middle of the first line instead.
BSI-Neuroinformatics
RIKEN BSI Research Database Portal — a Vite + React + TypeScript single page application.
Requirements
- Node.js 22.22.2+ / 24.15+ / 26+ (developed on Node 24)
- npm
Setup
npm install
The converted XOOPS data (modules/, uploads/) is not tracked in git and is not part of the
build. It lives in static-contents/, which sits next to the repository rather than inside it:
bsi-ni.brain.riken.jp/
├── bsi-ni.brain.riken.jp/ this repository
└── static-contents/ modules/ and uploads/
The dev and preview servers mount that directory at the site root, so nothing needs to be
linked into public/. It is deliberately kept out of dist/: it runs to gigabytes, and the
web server maps it in directly.
That data is produced by xoops-static-exporter
(sites/bsi-ni.brain.riken.jp/).
Scripts
npm run dev
Starts the Vite dev server on http://localhost:5173 with hot module replacement.
npm run build
Type-checks the project (tsc -b) and builds the production bundle into dist/.
npm run preview
Serves the contents of dist/ locally to verify a production build.
npm run lint / npm run format / npm run check
Runs Biome (biome.json): lint reports problems, format rewrites
formatting, and check applies both plus import sorting.
Deployment
Build locally and copy the result up; the server does not need a toolchain.
1. Build and check
npm ci
npm run check # biome, writes fixes
npm run build # runs tsc -b, then vite build
npm run preview # localhost:4173 , serves dist/ + static-contents/
dist/ is a few MB. static-contents/ is never part of it — the dev and preview servers mount it,
and in production the web server maps it in.
2. Send up the archive data
modules/ and uploads/, about 1.1 GB. The server mirrors the local layout: static-contents/ sits
next to the document root. Run this from the directory that holds both the repository and
static-contents/:
/data/www/bsi-ni.brain.riken.jp/
├── html/ document root
└── static-contents/
rsync -a --delete static-contents/ \
user@server:/data/www/bsi-ni.brain.riken.jp/static-contents/
3. Push the build
rsync -a --delete dist/ user@server:/data/www/bsi-ni.brain.riken.jp/html/
--delete clears out the previous build, and with it the data symlinks from step 4 — that is
deliberate, they are recreated right after. Anything left over from an older deployment goes too.
4. Link the archive data into the document root
cd /data/www/bsi-ni.brain.riken.jp/html
ln -s ../static-contents/* .
That links every top-level entry of static-contents/ — currently modules/ and uploads/:
/data/www/bsi-ni.brain.riken.jp/html/
├── index.html from dist/
├── assets/ from dist/
├── .htaccess from dist/
├── modules -> ../static-contents/modules
└── uploads -> ../static-contents/uploads
The glob keeps the set in step and picks up anything added to static-contents/ later. Note that
it skips dotfiles, of which there are none today. The links are relative, so the whole tree can be
moved without rewriting them.
Run steps 3 and 4 back to back. In between, /modules/... and /uploads/... are 404 because
step 3 removed the links; that is why plain ln -s works here without -f, and why a stale or
wrong link repairs itself on the next deploy.
Apache needs Options FollowSymLinks, AllowOverride All and Require all granted on the
document root, and Options FollowSymLinks with Require all granted on static-contents/.
Use symlinks rather than Alias. An Alias /modules ... maps the URL outside the document root,
so .htaccess no longer applies to it and every legacy /modules/<module>/*.php URL stops
reaching the SPA redirect handler. With symlinks the existing precedence still holds: real files
are served directly, everything else falls through to index.html.
Because the app uses HTML5 history routing, the web server must fall back to index.html for
unknown paths — see public/.htaccess.
5. Check the deployed site
curl -I https://bsi-ni.brain.riken.jp/modules/xoonips/tree.json # 200
curl -I https://bsi-ni.brain.riken.jp/database/file/1.png # 301
Then load /, /dbtools, /news and an item page in a browser and confirm the images resolve.