pixelize-authenticator
v2.2.7
Published
Shared auth screens (`Signin`, `Register`, `ForgetPassword`) for the Pixelize apps.
Readme
pixelize-authenticator
Shared auth screens (Signin, Register, ForgetPassword) for the Pixelize apps.
Bundle size — import only what you need
The package ships sideEffects: false and per-entry subpaths, so a bundler can drop the screens
you don't render instead of pulling the whole barrel:
// Eager code (app boot, auth check) — zero UI weight, no Chakra/design-library import
import { getToken, setToken, removeToken } from "pixelize-authenticator/token";
// Lazy-loaded route — isolates the Signin/Chakra/formik/yup graph into its own chunk
const Signin = lazy(() => import("pixelize-authenticator/signin"));
const Register = lazy(() => import("pixelize-authenticator/register"));
const ForgetPassword = lazy(() => import("pixelize-authenticator/forgot-password"));Importing getToken/setToken/removeToken from the root barrel (pixelize-authenticator)
alongside a static import of Signin/Register/ForgetPassword anywhere in the same app defeats
lazy-loading — a bundler can't split a single module in two, so if anything imports the barrel
eagerly (e.g. an app-boot auth check), the whole barrel — screens included — ships in that eager
chunk regardless of where else it's dynamically imported. Use ./token for the eager path and the
per-screen subpaths for the lazy one to keep them in separate chunks. The root . export is
unchanged and still works for apps that don't code-split.
react, react-dom and @chakra-ui/react are peer dependencies — the host app supplies them,
so its own installed copies are reused instead of a second copy landing in node_modules.
pixelize-design-library is supplied by the host too, but is deliberately declared nowhere —
not a dependency, not even an optional peer. Declaring it broke npm install on four of six
consumers, and no semver range can fix it: several apps pin a prerelease (2.4.2-beta.0), and npm
resolves peers without includePrerelease, so a prerelease satisfies a range only when some
comparator shares its exact major.minor.patch. >=2.3.2, >=2.3.2-0 and even * all reject
2.4.2-beta.0. Marking the peer optional doesn't help either — npm still enforces an optional
peer when the package is present in the tree, which it always is. src/package-exports.test.ts
guards this; see "Peer dependencies" below.
Color mode (light / dark)
The screens follow the host app automatically. PixelizeThemeProvider from
pixelize-design-library mirrors the resolved mode onto <html data-mode="light|dark">, and the
auth shell keys its own tokens off that attribute — so an app that already wraps itself in that
provider gets a dark login page with no change at its call sites.
Pass mode only to override the host:
<Signin mode="dark" getBaseUrl={…} getUserToken={…} getNavigation={…} />| mode | Result |
| --- | --- |
| omitted (default) | Follows the host app's <html data-mode>; light when there is none |
| "light" / "dark" | Forced, ignores the host |
| "system" | Follows the viewer's OS prefers-color-scheme |
Notes:
- Light is unchanged. Dark styling is additive — an app that never sets a mode and never mounts the provider renders exactly as it did before.
- Dark surfaces read
--color-*custom properties published byPixelizeThemeProvider, so they match the host's brand-resolved palette; the literals inwrapperStyles.tsare only a fallback for apps without the provider. - The design-library components inside the form (inputs, buttons) are themed by the host's own
provider, which requires the host to dedupe
pixelize-design-libraryto a single copy — already the case in account-frontend, crm-frontend and tickets via theirvite.config.ts.
Styling contract
Three rules that are easy to break silently. Each was a shipped bug.
1. A var the dark block re-points must be declared in wrapperStyles.ts, never in
brandCssVars. brandCssVars is spread onto .lf-shell as an inline style attribute, and an
inline custom property beats every stylesheet selector. --brand-link originally lived there and
the .lf-shell[data-mode="dark"] override could never win.
2. Never colour text with the raw brand. brand.c is picked to sit on white. Use
var(--brand-link), which is darkened for light and lightened for dark:
| | value | worst brand |
| --- | --- | --- |
| light | color-mix(in srgb, var(--brand) 75%, #000) | 4.84:1 |
| dark | color-mix(in srgb, var(--brand) 55%, #fff) | 4.97:1 |
Those percentages are the lightest touch that clears WCAG AA (4.5:1) for all seven brands on every
surface — emerald, rosewood and radiant fail on white at full strength, skyline fails on dark.
--text-muted is #687085 for the same reason (#727a89 measured 4.32:1). Re-run the numbers
before changing any of them.
3. Inject CSS at module scope, not from an effect. Effects run after the browser paints, so an
effect-injected <style> let the shell render one frame with no grid and no surface. That was
~15ms on account and crm — about one 60fps frame, which is exactly why they flickered and tickets
(~5ms) did not.
Development
Toolchain is Vite + Vitest + ESLint (flat config). react-scripts was removed — it pulled
1305 packages and most of the repo's vulnerability surface while powering only unused scripts.
npm start # Vite playground (src/index.js -> src/App.js)
npm run build # clean + tsc -> dist/ + copy assets
npm run test # vitest watch
npm run test:run # vitest once (CI form)
npm run lint # eslint src
npm run typecheck # tsc --noEmit
npm run verify # lint + typecheck + tests + build — the full gate
npm run package # verify, then version patch + publishnpm run package runs verify first, so a broken build or failing test blocks the publish
rather than shipping.
Publishing
CI auto-publish (.github/workflows/publish.yml) fires on push to develop, but GitHub
Actions is currently blocked org-wide on billing, so releases are done manually — same as
Micro-Components:
npm run package
git push origin develop --follow-tagsPeer dependencies
Every consumer already depends on pixelize-design-library directly, so leaving it undeclared costs
nothing at install time and resolves to the host's single copy at build time. If a future consumer
somehow lacks it, the failure is a loud unresolved-import at build, not a silent runtime break.
typesVersions is what makes the subpaths work on older consumers
crm-frontend and account-frontend both set "moduleResolution": "node", which ignores the
exports map entirely — import Signin from "pixelize-authenticator/signin" type-resolves to
nothing (TS2307) even though the bundler resolves it fine. The typesVersions block maps each
subpath to its .d.ts using the old node10 lookup, so those repos need no tsconfig change.
Verified against TS 4.9.5; keep the two maps in sync — a test asserts they point at the same files.
The exports map is an allowlist
package.json exports lists every importable path. Anything not listed becomes
unresolvable — adding the map already came within one commit of breaking crm-frontend,
which deep-imports pixelize-authenticator/dist/Signin/SigninProps. The ./dist/*
passthrough is what keeps such imports working; src/package-exports.test.ts guards it.
Add a named subpath for any new public entry point.
