-
|
I realize this is not hard to express in CSS but in StyleX it's a little tricky because you need to create a derived Here's a quick video of the intended use case which uses the code I will provide below: Built-in.Retina.Display.mp4Implementation looks roughly like this: import * as stylex from "@stylexjs/stylex";
import * as Lucide from "lucide-react";
import * as React from "react";
import { resetStyles } from "../stylex/reset-styles";
import { borderRadii } from "../stylex/themes.stylex";
import type { WithStyleXNativeProps } from "../stylex/with-stylex";
import { derivedTheme, theme } from "./nav3.stylex";
const layoutStyles = stylex.create({
center: {
alignItems: "center",
blockSize: "100dvh", // Viewport
display: "flex",
justifyContent: "center",
},
column: {
alignItems: "center", // Ensure children are centered (vertically)
display: "flex",
flexDirection: "column",
gap: 24,
},
row: {
display: "flex",
flexDirection: "row", // Make explicit
gap: 12,
justifyContent: "center",
},
});
const swatchStyles = stylex.create({
swatch: (color: stylex.CSSProperties["backgroundColor"]) => ({
backgroundColor: color,
blockSize: 32,
borderRadius: borderRadii.max,
inlineSize: 32,
}),
isActive: (color: stylex.CSSProperties["backgroundColor"]) => ({
boxShadow: `0 0 0 2px ${theme.baseColor}, 0 0 0 5px ${color}`,
}),
});
const buttonStyles = stylex.create({
button: {
// Flexbox
alignItems: "center",
display: "flex",
gap: 8,
// Box model
backgroundColor: {
default: theme.baseColor,
":hover": derivedTheme.themeColorStep1,
":active": derivedTheme.themeColorStep2,
},
borderRadius: borderRadii.max,
boxShadow: `0 0 0 1px ${derivedTheme.themeColorStep3}`,
paddingBlock: 4,
paddingInline: 12,
},
icon: {
blockSize: 16,
color: derivedTheme.themeColor,
inlineSize: 16,
},
text: {
color: derivedTheme.themeColor,
fontWeight: 500,
},
});
////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////
type SwatchProps = WithStyleXNativeProps<
React.ButtonHTMLAttributes<HTMLButtonElement>
> & {
isActive: boolean;
color: stylex.CSSProperties["backgroundColor"];
};
function Swatch(props: SwatchProps): React.ReactNode {
return (
<button
{...props._nativeProps}
{...stylex.props(
resetStyles.button,
swatchStyles.swatch(props.color),
props.isActive && swatchStyles.isActive(props.color),
props._stylex,
)}
/>
);
}
type ButtonProps = WithStyleXNativeProps<
React.ButtonHTMLAttributes<HTMLButtonElement>
>;
function Button(props: ButtonProps) {
return (
<button
{...props._nativeProps}
{...stylex.props(resetStyles.button, buttonStyles.button, props._stylex)}
>
<Lucide.Hand {...stylex.props(buttonStyles.icon)} />
<span {...stylex.props(buttonStyles.text)}>Press me</span>
</button>
);
}
// Unwrap a CSS variable
function unwrap(var_: `var(${string})` | string): string {
if (var_.startsWith("var(") && var_.endsWith(")")) {
return var_.slice(4, -1);
}
return var_;
}
export function Nav3(): React.ReactNode {
const [hue, setHue] = React.useState(0);
// const presets: number[] = [0, 36, 72, 108, 144, 180, 216, 252, 288, 324];
const presets = Array.from({ length: 10 }, (_, i) => i * 36);
console.log(theme.themeColor);
return (
<>
{/* Interpolate the custom property (e.g. unwrap) and set the value */}
<style>
{`
:root {
${unwrap(theme.themeColor)}: hsl(${hue}, 100%, 50%);
}
`}
</style>
{/* Content */}
<div {...stylex.props(layoutStyles.center)}>
<div {...stylex.props(layoutStyles.column)}>
<input
type="range"
min={0}
max={360}
step={36}
value={hue}
onChange={(e) => setHue(Number(e.target.value))}
/>
<div {...stylex.props(layoutStyles.row)}>
{presets.map((preset) => (
<Swatch
key={preset}
_nativeProps={{ onClick: () => setHue(preset) }}
isActive={preset === hue % 360}
color={`hsl(${preset}, 100%, 50%)`}
/>
))}
</div>
<div {...stylex.props(layoutStyles.row)}>
<Button />
<Button />
<Button />
<Button />
</div>
</div>
</div>
</>
);
}import * as stylex from "@stylexjs/stylex";
export const theme = stylex.defineVars({
baseColor: "black",
themeColor: "hsl(210, 100%, 50%)", // Treat this as the default value
});
// Derived variables from theme
export const derivedTheme = stylex.defineVars({
themeColor: theme.themeColor,
themeColorStep1: `color-mix(in oklch, transparent, ${theme.themeColor} 10%)`,
themeColorStep2: `color-mix(in oklch, transparent, ${theme.themeColor} 20%)`,
themeColorStep3: `color-mix(in oklch, transparent, ${theme.themeColor} 30%)`,
themeColorStep4: `color-mix(in oklch, transparent, ${theme.themeColor} 40%)`,
themeColorStep5: `color-mix(in oklch, transparent, ${theme.themeColor} 50%)`,
themeColorStep6: `color-mix(in oklch, transparent, ${theme.themeColor} 60%)`,
themeColorStep7: `color-mix(in oklch, transparent, ${theme.themeColor} 70%)`,
themeColorStep8: `color-mix(in oklch, transparent, ${theme.themeColor} 80%)`,
themeColorStep9: `color-mix(in oklch, transparent, ${theme.themeColor} 90%)`,
});Is there a better way to do this that would be more idiomatic in StyleX or is this a perfectly reasonable approach? The only part that makes me slightly nervous is this: <style>
{`
:root {
${unwrap(theme.themeColor)}: hsl(${hue}, 100%, 50%);
}
`}
</style>I tried doing something similar with Thank you. |
Beta Was this translation helpful? Give feedback.
Replies: 1 comment 2 replies
-
|
Will write a longer answer later, but start by converting ‘derivedTheme’ to use defineConsts or writing the color-mix() calls directly where you need them. |
Beta Was this translation helpful? Give feedback.
Will write a longer answer later, but start by converting ‘derivedTheme’ to use defineConsts or writing the color-mix() calls directly where you need them.