Comments (27)
There's a lot here already, so I'll try to just answer in the initial question. Using dark mode based on a user controlled data-id
. This can be done by creating default variables with defineVars
and then creating separate themes with createTheme
.
Something like:
// vars.stylex.js
import * as stylex from '@stylexjs/stylex';
export const colors = stylex.defineVars({
primary: 'red',
// ...
});
// color-themes.js
import * as stylex from '@stylexjs/stylex';
import {colors} from './vars.stylex.js';
export const darkTheme = stylex.createTheme(colors, {
primary: 'blue',
// ...
});
// layout.tsx
import {darkTheme} from './color-themes';
export default function RootLayout({isDarkMode, children}) {
return (
<html {...stylex.props(isDarkMode && darkTheme)}>
<head>{/* ... */}</head>
<body>
{children}
</body>
</html>
);
}
Now you can just import and use the variables from vars.stylex.ts
directly
and use them. They will resolve to light mode or not based on whether
the theme is set on the html
tag or not.
Things to note:
- You don't need the
data-id
for StyleX, but you can keep it if you want it for other things. - The
layout.tsx
is an example for how this might be done in NextJS. You can set the theme
on thehtml
element any way you want. e.g. If you're using DOM APIs for this, that will work too. - Remember that you must always import the variables from the original
defineVars
export.
If you want a "light" theme, a "dark" theme, and a default theme that uses @media
to change automatically you can do that too. Change the original variables defined with defineVars
to use media queries, then create two themes for light and dark that don't use media queries.
from stylex.
Or, to generate a style like:
:root {
--primary: red;
--primary-dark: blue;
}
.myComponent {
color: var(--primary);
}
:is([data-mode="dark"]) .myComponent {
color: var(--primary-dark);
}
Maybe from something like:
// tokens.stylex.js
import * as stylex from '@stylexjs/stylex';
export const colors = stylex.defineVars({
primary: 'red',
'primary-dark': 'blue',
});
// MyComponent.jsx
import { colors } from './tokens.stylex';
const styles = stylex.create({
myComponent: {
color: {
default: colors.primary,
':is[dark-mode="dark"]': colors['primary-dark'],
}
},
});
// ...
from stylex.
I didn't cover the JS bits but @kevintyj's suggestion is valid for React.
This way you would not have to create one defineVars and two separate themes to use it with @media
I'm not clear what this means so I'll try to clarify what I meant by multiple themes.
Case 1: Light Theme is default. Dark theme can be enabled manually
Use defineVars
to create the default light theme, use createTheme
to create a dark theme. Apply dark
theme conditionally as suggested by @kevintyj
Case 2: System theme is default. Light and dark themes can be enabled manually
Use defineVars
and use the media query to make this theme adapt to the media query. Use createTheme
to create the "light" and "dark" themes that always use the light or dark colors respectively.
from stylex.
hey @olivierpascal
how about using the required component as nested object
psuedoSelector(dark).myComponent : colorValue
psuedoSelector(light).myComponent : colorValue
for this there is a reference on docs, see if it helps
defining styles
correct me if it doesn't relate to your usecase
from stylex.
@nmn your solution works like a charm, thank you. I have nothing to add, maybe we can close this topic? Thanks all very much.
from stylex.
I had this bug the very first time I used stylex (with some code straight from the doc) but, as I stopped using media queries, I forgot to create an issue.
On my side, the following does work:
export const colorsRepo = {
background: 'red',
};
export const colorsRepo = {
background: {
default: 'red', // work
whatever: 'lightgray',
},
};
When the following silently fail (the entire var group is not set):
export const colorsRepo = {
background: {
default: 'red',
'@media (prefers-color-scheme: dark)': 'lightgray',
},
};
If you can reproduce this bug, maybe create a new issue with a minimal reproducible code?
from stylex.
I'm using Astro and during server-side rendering, dark mode is unknown so it defaults to light mode.
This is the problem here. We have media queries for detecting dark mode. You shouldn't be trying to choose light mode or dark mode on the server at all.
I'm working on adding first-class support for light-dark()
. Which will make doing this even more flexible.
But TLDR; Setting themes based on selectors such as html[data-theme='dark']
should be avoided as a best practice. But even if you're using them, they should only be used when the user has manually overridden their preferred color scheme which should be known on the server. If you don't know their preferred color scheme, you should be using Media Queries to choose automatically. (And use light-dark()
in the future)
from stylex.
FWIW, the stylex website uses html[data-theme=dark].
The only SSR-safe ways of doing color theming is:
- To store the user pref
- Use media queries
Docusaurus generates a static bundle so there is no server to store user preferences on.
:(
from stylex.
You can have a look at the Next.js example to see how it might be done. But I don't think it works like you want to make it work in styleX.
I don't know if this is your question or not, but try to be explicit about what you want support for. Or describe the solution you're looking for.
from stylex.
Of course: for the needs of my app, i need to be able to demo a component in both light and dark mode on the same screen.
<div data-mode="light">
<MyStyledComponent className="myComponent" />
</div>
<div data-mode="dark">
<MyStyledComponent className="myComponent" />
</div>
I cannot use @media (prefers-color-scheme: light/dark)
because this is OS specific (unaffected by the color-scheme
css property).
I would like to be able to generate a style like:
:root {
--primary: red;
}
:is([data-mode="dark"]) {
--primary: blue;
}
.myComponent {
color: var(--primary);
}
Maybe from something like:
// tokens.stylex.js
import * as stylex from '@stylexjs/stylex';
export const colors = stylex.defineVars({
primary: {
default: 'red',
':is[dark-mode="dark"]': 'blue',
},
});
// MyComponent.jsx
import { colors } from './tokens.stylex';
const styles = stylex.create({
myComponent: {
color: colors.primary,
},
});
// ...
What do you think?
from stylex.
I know this syntax would conflict with pseudo classes, but you get the idea.
from stylex.
Mmmh, the problem is that the pseudo selector :is([dark-mode="dark"])
should be at the root level and hence placed before the class name, like :is([data-mode="dark"]) .myComponent {}
or :is([data-mode="dark"] .myComponent) {}
.
I don't think it's possible to do so with a nested object, at the component level.
from stylex.
I think for my use case, the best API would be something like:
// tokens.stylex.js
import * as stylex from '@stylexjs/stylex';
export const colors = stylex.defineVars({
primary: {
default: 'red',
':is[dark-mode="dark"]': 'blue',
},
secondary: {
default: 'green',
':is[dark-mode="dark"]': 'black',
},
});
-or-
// tokens.stylex.js
import * as stylex from '@stylexjs/stylex';
export const colors = stylex.defineVars({
default: {
primary: 'red',
secondary: 'green',
},
':is[dark-mode="dark"]': {
primary: 'blue',
secondary: 'dark',
},
});
to generate:
/* stylex.css */
:root {
--primary: red;
--secondary: green;
}
:is([data-mode="dark"]) {
--primary: blue;
--secondary: black;
}
The remaining question would still be how to do the same at the component level, without the theming API.
from stylex.
Sorry to spam. I just see that https://stylexjs.com stylesheet is compiling to this:
:root {
--ifm-color-primary: #872291;
/* ... */
}
html[data-theme=dark]:root {
--ifm-color-primary: #be51c8;
/* ... */
}
I don't know if it uses stylex or pure css. Probably a mix of both.
from stylex.
Just to add on to the conversation, you can also use a Context (if you are using React or Nextjs) to switch between themes globally similar to most other styling libraries out there
const ThemeContext = React.createContext(undefined)
...
const [theme, setTheme] = useState('light');
...
// You can also append props to children directly through cloneElement (but is discouraged by React team)
<ThemeContext.Provider value={ theme }>
<body {...props(theme === 'dark' && darkTheme)}>
{children}
</body>
</ThemeContext.Provider>
You can also use custom hooks and an effect to automatically set default theme and persist data to local storage.
React docs: https://react.dev/reference/react/createContext shows the same exact application method.
This way you would not have to create one defineVars
and two separate themes
to use it with @media
, you can just create the same defineVars with an added theme as mentioned above by @nmn, but this may not be a recommended use pattern (and why he did not cover it).
from stylex.
@nmn If we possess numerous properties but wish to override certain values, what would be the most effective method to accomplish this? I used ...colors.__tokens
but it didn't work, so I have to export base values.
Here is the code I've written, but I think it's not quite right.
// tokens.stylex.tsx
import * as stylex from '@stylexjs/stylex';
const DARK = '@media (prefers-color-scheme: dark)';
export const colorsRepo = {
primaryText: { default: 'black', [DARK]: 'white' },
secondaryText: { default: '#333', [DARK]: '#ccc' },
accent: { default: 'blue', [DARK]: 'lightblue' },
background: { default: 'white', [DARK]: 'black' },
lineColor: { default: 'gray', [DARK]: 'lightgray' },
borderRadius: '4px',
fontFamily: 'system-ui, sans-serif',
fontSize: '16px',
};
export const colors = stylex.defineVars(colorsRepo);
export const spacing = stylex.defineVars({
none: '0px',
xsmall: '4px',
small: '8px',
medium: '12px',
large: '20px',
xlarge: '32px',
xxlarge: '48px',
xxxlarge: '96px',
});
//themes.ts
import * as stylex from '@stylexjs/stylex';
import { colors, colorsRepo } from './tokens.stylex';
// A constant can be used to avoid repeating the media query
const DARK = '@media (prefers-color-scheme: dark)';
// Dracula theme
export const dracula = stylex.createTheme(colors, {
...colorsRepo,
primaryText: { default: 'purple', [DARK]: 'lightpurple' },
background: { default: '#555', [DARK]: 'black' },
});
// MyComponent.tsx
'use client';
import * as stylex from '@stylexjs/stylex';
import { colors, spacing } from './tokens.stylex';
import { dracula } from './themes';
import { useState } from 'react';
const styles = stylex.create({
container: {
color: colors.primaryText,
backgroundColor: colors.background,
padding: spacing.medium,
marginTop: '10px',
fontSize: colors.fontSize,
},
});
export const MyComponent = ({ children }: any) => {
const [isDark, setDark] = useState(false);
const toggle = () => {
setDark(!isDark);
};
return (
<div {...stylex.props(isDark && dracula)}>
<div {...stylex.props(styles.container)}>
<button onClick={toggle}>Toggle</button>
{children}
</div>
</div>
);
};
And one more thing, the class for debug mode is
UnknownFile__dracula
I test with https://github.com/facebook/stylex/tree/main/apps/nextjs-example)
<div class="x1wm1iiy UnknownFile__dracula">
<button>Toggle</button>
<div class="MyComponent__styles.container x13ca1vq x1lfge5 x1nq940n xf0e2mi">Hello
</div>
</div>
![Screenshot 2023-12-21 at 21 58 16](https://private-user-images.githubusercontent.com/61301244/292239924-b42883c1-e93e-45cf-84b2-ff5417a25996.png?jwt=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJnaXRodWIuY29tIiwiYXVkIjoicmF3LmdpdGh1YnVzZXJjb250ZW50LmNvbSIsImtleSI6ImtleTUiLCJleHAiOjE3MTI3NDI0ODUsIm5iZiI6MTcxMjc0MjE4NSwicGF0aCI6Ii82MTMwMTI0NC8yOTIyMzk5MjQtYjQyODgzYzEtZTkzZS00NWNmLTg0YjItZmY1NDE3YTI1OTk2LnBuZz9YLUFtei1BbGdvcml0aG09QVdTNC1ITUFDLVNIQTI1NiZYLUFtei1DcmVkZW50aWFsPUFLSUFWQ09EWUxTQTUzUFFLNFpBJTJGMjAyNDA0MTAlMkZ1cy1lYXN0LTElMkZzMyUyRmF3czRfcmVxdWVzdCZYLUFtei1EYXRlPTIwMjQwNDEwVDA5NDMwNVomWC1BbXotRXhwaXJlcz0zMDAmWC1BbXotU2lnbmF0dXJlPTcwZDlkNmI4Njg0MDMyZDRkYmY4OWU4ZTMyN2RlOGUyM2JkZjgwMDlhZTM4NzljZjVlMTgwODAzMmJmMjY3NTUmWC1BbXotU2lnbmVkSGVhZGVycz1ob3N0JmFjdG9yX2lkPTAma2V5X2lkPTAmcmVwb19pZD0wIn0.d1oBa5VurxxqJiJYCse9qCtoycX6iQMQ_kUdaWJjjC0)
from stylex.
If we possess numerous properties but wish to override certain values, what would be the most effective method to accomplish this?
This was a type constraint that I fixed yesterday. In the next version, you'll be able to override only some of the variables when creating a theme.
@olivierpascal Could you give me a slightly more detailed example?
from stylex.
Ok @nmn, here is a full reproducible example:
// tokens.stylex.ts
import * as stylex from '@stylexjs/stylex';
// A constant can be used to avoid repeating the media query
const DARK = '@media (prefers-color-scheme: dark)';
export const colors = stylex.defineVars({
primary: { default: 'blue', [DARK]: 'green' }, // NOT OK: text output is black
// primary: 'blue', // OK: text output is blue
// primary: { default: 'blue', whatever: 'green' }, // OK: text output is blue
});
// MyComponent.tsx
import * as stylex from '@stylexjs/stylex';
import React from 'react';
import { colors } from './tokens.stylex';
const styles = stylex.create({
container: {
color: colors.primary,
},
});
export const MyComponent: React.FC = () => (
<div {...stylex.props(styles.container)}>TEXT</div>
);
The CSS var is not compiled.
![image](https://private-user-images.githubusercontent.com/2182039/292381285-9dc84c2c-9cbe-4292-a3a4-558c4059b63e.png?jwt=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJnaXRodWIuY29tIiwiYXVkIjoicmF3LmdpdGh1YnVzZXJjb250ZW50LmNvbSIsImtleSI6ImtleTUiLCJleHAiOjE3MTI3NDI0ODUsIm5iZiI6MTcxMjc0MjE4NSwicGF0aCI6Ii8yMTgyMDM5LzI5MjM4MTI4NS05ZGM4NGMyYy05Y2JlLTQyOTItYTNhNC01NThjNDA1OWI2M2UucG5nP1gtQW16LUFsZ29yaXRobT1BV1M0LUhNQUMtU0hBMjU2JlgtQW16LUNyZWRlbnRpYWw9QUtJQVZDT0RZTFNBNTNQUUs0WkElMkYyMDI0MDQxMCUyRnVzLWVhc3QtMSUyRnMzJTJGYXdzNF9yZXF1ZXN0JlgtQW16LURhdGU9MjAyNDA0MTBUMDk0MzA1WiZYLUFtei1FeHBpcmVzPTMwMCZYLUFtei1TaWduYXR1cmU9NmFjMGE1YmIxYjlkZjgzZWM1YjFiMGRkYmM2NDM1NDdjNWUyMzQ4NGZiZWRlN2E1NmU5Njc3ZDZhMjc1NTc1MyZYLUFtei1TaWduZWRIZWFkZXJzPWhvc3QmYWN0b3JfaWQ9MCZrZXlfaWQ9MCZyZXBvX2lkPTAifQ._htWUalYDo8CRMzNBR7LqiInpp99jSwk6KbUhQGUBFg)
from stylex.
Probably related to #235
from stylex.
@olivierpascal Yup, I think this is caused by #235 as well. Can you try a production build to verify in your case as well?
from stylex.
v0.4.1 Fixes the issues mentioned.
from stylex.
for me this is working both with context and prefers-color-scheme.
//themes.ts
const DARK = '@media (prefers-color-scheme: dark)';
export const lightTheme = stylex.createTheme(colors, {
primary: 'blue',
bg: {
default: "white",
[DARK]: "white",
}
})
export const darkTheme = stylex.createTheme(colors, {
primary: 'blue',
bg: {
default: "black",
[DARK]: "black",
}
})
how can i make it work without the colors object, If possible. I couldn't get the red or blue to appear both changing the prefers-color-scheme though the browser and by changing the theme via context.
export const colors = stylex.defineVars({
primary: 'red',
bg: {
default: "red",
[DARK]: "blue",
}
});
then i made this provider like suggested above:
//providers.tsx
type Theme = 'light' | 'dark'
interface ThemeContextType {
theme: Theme
setTheme: Dispatch<SetStateAction<Theme>>
}
export const ThemeContext = createContext<ThemeContextType | undefined>(undefined)
export const ThemeProvider = ({ children }: Props) => {
const [theme, setTheme] = useState<Theme>('dark')
const contextValue: ThemeContextType = { theme, setTheme }
return (
<ThemeContext.Provider value={contextValue}>
<html {...stylex.props(s.html, s.reset)} lang="en">
<body {...stylex.props(s.reset, s.body, theme === 'dark' ? darkTheme : lightTheme)}>
{children}
</body>
</html>
</ThemeContext.Provider>
)
}
export const useTheme = () => {
const context = useContext(ThemeContext)
if (!context) {
throw new Error('useTheme must be used within a ThemeProvider')
}
return context
}
//component.ts
const { setTheme } = useTheme();
//layout.ts
export default async function RootLayout({ children }: { children: React.ReactNode }) {
return (
<ThemeProvider>
{children}
</ThemeProvider>
)
}
from stylex.
how can i make it work without the colors object, If possible
@alejandroyunes I don't understand what you're asking.
from stylex.
hi,
yes I think what I was asking is that if primary isnt any of the createThemplate it takes what is in the colors object in defineVars,
//themes.ts
const DARK = '@media (prefers-color-scheme: dark)';
export const lightTheme = stylex.createTheme(colors, {
bg: {
default: "white",
[DARK]: "white",
}
})
export const darkTheme = stylex.createTheme(colors, {
bg: {
default: "black",
[DARK]: "black",
}
})
export const colors = stylex.defineVars({
primary: 'red',
bg: {
default: "red",
[DARK]: "blue",
}
});
why do I need the bg in colors and in both themes?
from stylex.
I still don't understand what you're asking, but you should be able to write this instead:
//themes.ts
const DARK = '@media (prefers-color-scheme: dark)';
export const lightTheme = stylex.createTheme(colors, {
bg: "white",
})
export const darkTheme = stylex.createTheme(colors, {
bg: "black",
})
export const colors = stylex.defineVars({
primary: 'red',
bg: {
default: "red",
[DARK]: "blue",
}
});
why do I need the bg in colors and in both themes?
You don't...
from stylex.
Sorry to bring up an old thread but I'm not able to do dark mode without FOUCs.
Given this example...
// layout.tsx
import {darkTheme} from './color-themes';
export default function RootLayout({isDarkMode, children}) {
return (
<html {...stylex.props(isDarkMode && darkTheme)}>
<head>{/* ... */}</head>
<body>
{children}
</body>
</html>
);
}
I'm using Astro and during server-side rendering, dark mode is unknown so it defaults to light mode. Now on client hydration we get the light mode rendered, then once the React code kicks in and recognizes that dark mode is set, it re-renders.
The way I got this working in other styling libraries is by using descendent selectors similar to html[data-theme='dark'] .test
. Even on the first render from SSR, the styles are correct. Is there an escape hatch here?
Thanks so much @nmn!
from stylex.
Gotcha, so the rec is to store user prefs for dark / light on server and then use that for server side rendering.
FWIW, the stylex website uses html[data-theme=dark]
.
from stylex.
Related Issues (20)
- Focus for input type range HOT 1
- Integration with Create React App by default, including types support for typescript HOT 2
- RFC: esbuild plugin HOT 2
- An option for the babel plugin that transforms the debug/dev classes. HOT 2
- Support Older React Versions HOT 3
- CSS Properties don't get written (esbuild) HOT 1
- Add ability to customise class names generation so we can use one letter classnames. HOT 5
- StyleX Plugin for Bun.sh Bundler HOT 6
- gridTemplateRows 0fr is being compiled to 0px, which breaks css transition HOT 4
- esbuild plugin with write:false should set file name HOT 3
- [RFC] Define value for multiple pseudo-classes at one HOT 2
- [@styles/open-props]: Has any plan/progress to support `ESM`? HOT 2
- Strange :not(#\\#) selector HOT 1
- Add option to support vertical writing modes in modern browsers
- How can I use container queries with StyleX? HOT 1
- I can't make changes to the interface (next.js & react) HOT 1
- Media query specificity order. Certian properties broken (`gridColumn`) HOT 3
- Only static values are allowed inside of a stylex.create() call. HOT 5
- Need help with Qwik.
- '0px' vars are compiled to '0' which leads to error at best, unpredicatable results at worst HOT 2
Recommend Projects
-
React
A declarative, efficient, and flexible JavaScript library for building user interfaces.
-
Vue.js
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
-
Typescript
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
-
TensorFlow
An Open Source Machine Learning Framework for Everyone
-
Django
The Web framework for perfectionists with deadlines.
-
Laravel
A PHP framework for web artisans
-
D3
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
-
Recommend Topics
-
javascript
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
-
web
Some thing interesting about web. New door for the world.
-
server
A server is a program made to process requests and deliver data to clients.
-
Machine learning
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
-
Visualization
Some thing interesting about visualization, use data art
-
Game
Some thing interesting about game, make everyone happy.
Recommend Org
-
Facebook
We are working to build community through open source technology. NB: members must have two-factor auth.
-
Microsoft
Open source projects and samples from Microsoft.
-
Google
Google ❤️ Open Source for everyone.
-
Alibaba
Alibaba Open Source for everyone
-
D3
Data-Driven Documents codes.
-
Tencent
China tencent open source team.
from stylex.