## Summary
Now that Twenty has fully migrated from Emotion to Linaria, the theme
system has been simplified to remove unnecessary complexity that existed
only to support the old runtime injection pattern.
### What changed
- **Deleted** `generateThemeConstants.ts` script and the entire
`generated/` directory — no more auto-generation
- **Added** `theme-light.css` and `theme-dark.css`: static CSS files
with 991 custom properties each, scoped under `.light` and `.dark`
selectors respectively
- **Moved** `themeCssVariables.ts` out of `generated/` and hand-maintain
it as a static `as const` object of `var(--t-*)` references (Linaria can
statically evaluate these at build time)
- **Extracted** numeric constants (`MOBILE_VIEWPORT`, `ICON_SIZES`,
`ICON_STROKES`) into a new `constants.ts` — CSS variables can't be used
in media queries or as numeric icon size props
- **Simplified** `ThemeContextProvider`: removed
`ThemeCssVariableInjectorEffect` entirely; now uses a single
`useLayoutEffect` to toggle `.light`/`.dark` class on `<html>`
- **Added** `class="light"` to `index.html` as default to prevent FOUC
before React hydration
### Why
The previous setup maintained a dual system: JS theme objects
(`THEME_LIGHT`/`THEME_DARK`) used at runtime, plus a generation script
that produced CSS variable entry arrays, which were then injected into
the DOM by `ThemeCssVariableInjectorEffect`. With Linaria, theme values
only need to be CSS custom properties — the JS objects were redundant.
This PR removes ~250 lines of infrastructure while keeping the same
theming capabilities.
# Introduction
close#9965
When landing on twenty you should be able to see a white screen
flickering if you had setup dark mode.
This is because before the SPA has been loaded we're not displaying
anything, which in a white screen from the browser.
During this period we should display a background color following the
user's device theme.
## Reproduction
In order to reproduce this behavior define a fast 4G connection from
your network console.
## Cons
Device mode might not the one chosen afterwards when the user has been
authenticated
=> We should store appearance settings in the local storage in order to
optimistically render the default "loading" background ( wouldn't be
100% bullet proof for instance if the user is now unauth for some reason
)
Body background will be override by theme after app bootstrap
This is the result of a long discussion we had here:
https://github.com/twentyhq/twenty/issues/8001.
The goal is to stop iOS from automatically zooming when the user focuses
on an input whose font size is less than 16px.
The options were:
1. Disable zoom for all devices
2. Disable zoom for devices detected as iOS devices, which doesn't
prevent users from zooming manually but fixes the auto-zoom bug
3. Set the font size of the inputs to be equal to or greater than
16px—this change would take a lot of time
To me, the second option is the best, as iOS prevents developers from
disabling zoom. They saw that it was overused and chose to restrict this
setting. Setting a `maximum-scale` doesn't prevent users from zooming,
but it fixes the initial bug we had.
My implementation can be seen as [progressive
enhancement](https://developer.mozilla.org/en-US/docs/Glossary/Progressive_Enhancement
): If we can detect that the user uses an iOS device, we'll set the
`maximum-scale` viewport property. Relying on the user agent is always
unstable, and the check might fail unpredictably. We might not disallow
auto-zoom for some iOS devices.
However, I think we can either:
- Invest some time to choose a more reliable user detection pattern if
the one I suggest is not sufficient ([we find many different checks on
the
internet](https://stackoverflow.com/questions/9038625/detect-if-device-is-ios),
I'm not sure which one is the best)
- Choose to apply the viewport setting to all devices and remove the JS
code. According to my tests, it doesn't prevent zooming on desktops.
However, it does on Android phones. I think it's not lovely to disallow
zoom, but if the team agrees that we should go this way, I won't
disagree.
I know my JavaScript code does not follow a pattern we want to spread in
the app. The synchronous script will run as soon as possible to ensure
the viewport is correctly set when the website launches. This shouldn't
be an example followed by others.
Thanks, @harshit078, for your help in thinking about the best option.
I'm tagging @lucasbordeau and @charlesBochet for a technical review.
I would appreciate if someone could test on a more recent iOS device
than mine.
Here is a demonstration of the behavior on iOS:
https://github.com/user-attachments/assets/d49fb65f-dd76-455c-9ac0-d4c002a7fe89