The main decisions behind the code, briefly.
vignette("customising") covers every configuration key.
Static site, no server
The site is plain HTML on GitHub Pages, so no
*.shinyInput() is used. Stateful components keep their
state in React instead:
| What | Component |
|---|---|
| Mobile navigation drawer | Drawer.triggerId() |
| Selected app and screenshot |
TabContext.static() /
TabList.static()
|
| Full-size screenshot | Dialog.triggerId() |
The showcase uses defaultValue, not value:
without an onChange, a controlled tab never moves.
One script
R/boot.R is the only <script>. It
removes the prerendered fallback once React has mounted, and highlights
the app bar tab of the band in view. Everything else works without
JavaScript: the scroll cue is a real link, and dark mode is a media
query.
Prerendered content
React draws the whole page, so without JavaScript the
<body> would be JSON. R/fallback.R
therefore writes a plain-HTML copy of the content (headings, text,
links) from the same items, and it is removed on mount. It costs a few
KB. R/seo.R writes the <head> metadata,
sitemap.xml and robots.txt.
Configuration over code
Sections live in mui.config.yml; nothing in
R/ names one. A section’s id is its anchor,
its tab target and the scroll cue target. Custom variants go in
variants.R beside the config, because an installed package
namespace is locked.
One palette
Colours are defined once in theme$palette (roles, not
hues). mui_theme() feeds MUI and
mui_css_root_vars() emits the same values as
--site-* CSS variables. palette_dark is merged
over palette, so it names only what differs. Component
overrides use var(--site-*), never a hex, so they work in
both schemes.