In my case I just want to change the default default color, take as example the default home section:
We recommend building UIs with a [**component-driven**](https://componentdriven.org)
That leads to:
I'd like to change that blue to, say, red.
I saw that the a class has the following themes:
class="sbdocs sbdocs-a css-19nbuh3"
So, I created a .storybook/manager-head.html and inside I wrote:
<style>
.sbdocs-a {
color: red;
}
But I see no changes! What am I doing wrong?
You can do this two ways but all in .storybook/preview.js.
Altering the docs theme:
export const parameters = {
...
docs: {
theme: {
colorSecondary: 'red',
},
},
...
};
This have the side effect to cause other unwanted components to change their color, as this color is not used only for links.
Provide your own <a> component to docs [best solution]:
export const parameters = {
...
docs: {
components: {
a: ({children, ...args}) => <a style={{color: 'red'}} {...args}>{children}</a>
},
},
...
};
This is the best way i found to style docs components.
also token name used for a can be found in storybook sources
See theming docs page for a full theme example.
Related
I migrated from Mui 4 to 5 and wonder how to use class names. If I want to apply certain styles to just one component there is the SX property. However, I'm struggling with using the same class for multiple components. In v4 my code looked like this:
export const useStyles = makeStyles((theme: Theme) =>
createStyles({
root: {
padding: theme.spacing(1),
margin: 'auto',
},
})
)
I could import this useStyles hook in any component and use it like this:
const classes = useStyles()
...
<div className={classes.root}>...</div>
This docs say, that I can 'override styles with class names', but they don't tell how to do it:
https://mui.com/customization/how-to-customize/#overriding-styles-with-class-names
Do I have to put these styles in an external CSS file?
.Button {
color: black;
}
I would rather define the styles in my ts file.
I also found this migration guide:
https://next.material-ui.com/guides/migration-v4/#migrate-makestyles-to-emotion
I don't like approach one, because using this Root wrapper, it is inconvenient to apply a class conditionally. (Especially for typescript there is some overhead) Approach two comes with an external dependency and some boilerplate code.
Ideally I would use styles like this, perhaps with one rapper function around the styles object:
export const root = {
padding: theme.spacing(1),
margin: 'auto',
}
<div className={root}>...</div>
Of course, the last approach doesn't work, because className wants a string as input. Does anybody know an alternative with little boilerplate code?
I suggest you take a look at emotion's documentations for details. The sx prop is actually passed to emotion.
You can do something like this:
const sx = {
"& .MuiDrawer-paper": {
width: drawerWidth
}
};
<Drawer sx={sx}/>
Equivalent to MUI v4
const useStyles = makeStyles({
drawerPaper: {
width: drawerWidth,
}
});
const classes = useStyles();
<Drawer
classes={{
paper: classes.drawerPaper,
}}
/>
Answering your exact question, there are use cases (I think yours is not one of them and you should use styled components) however for those like me who stumble upon it and want a "exact answer to this question" and not a "do this instead", this is how you achieve to retrieve the class names.
This is so far undocumented.
For functional components, using emotion, here an use case where you have a 3rd party component that expects, not one, but many class names, or where the className property is not where you are meant to pass the property.
import { css, Theme, useTheme } from "#mui/material/styles";
import { css as emotionCss } from "#emotion/css";
const myStyles = {
basicClass: {
marginLeft: "1rem",
marginRight: "1rem",
paddingLeft: "1rem",
paddingRight: "1rem",
},
optionClass: (theme: Theme) => ({
[theme.breakpoints.down(theme.breakpoints.values.md)]: {
display: "none",
}
})
}
function MyComponent() {
cons theme = useTheme();
// first we need to convert to something emotion can understand
const basicClass = css(myStyles.basicClass);
const optionClass = css(myStyles.optionClass(theme));
// now we can pass to emotion
const basicClassName = emotionCss(basicClass.styles);
const optionClassName = emotionCss(optionClass.styles);
return (
<ThirdPartyComponent basicClassName={basicClassName} optionClassName={optionClassName} />
)
}
When you have a Class Component, you need to use the also undocumented withTheme from #mui/material/styles and wrap your class, if you use the theme.
WHEN IT IS NOT AN USE CASE
When your component uses a single className property just use styled components.
import { styled } from "#mui/material/styles";
const ThrirdPartyStyled = styled(ThirdPartyComponent)(({theme}) => ({
color: theme.palette.success.contrastText
}))
Even if you have dynamic styles
import { styled } from "#mui/material/styles";
interface IThrirdPartyStyledExtraProps {
fullWidth?: boolean;
}
const ThrirdPartyStyled = styled(ThirdPartyComponent, {
shouldForwardProp: (prop) => prop !== "fullWidth"
})<IThrirdPartyStyledExtraProps>(({theme, fullWidth}) => ({
color: theme.palette.success.contrastText,
width: fullWidth ? "100%" : "auto",
}))
Even if each one has some form of custom color, you just would use "sx" on your new ThrirdPartyStyled.
When you are just trying to reuse a style around (your use case)
const myReusableStyle = {
color: "red",
}
// better
const MyStyledDiv = styled("div")(myReusableStyle);
// questionable
const MySpanWithoutStyles = styled("span")();
// better
const MyDrawerStyled = styled(Drawer)(myReusableStyle);
function MyComponent() {
return (
<MyStyledDiv>
questionable usage because it is less clean:
<MySpanWithoutStyles sx={myReusableStyle}>hello</MySpanWithoutStyles>
<MySpanWithoutStyles sx={myReusableStyle}>world</MySpanWithoutStyles>
these two are equivalent:
<MyDrawerStyled />
<Drawer sx={myReusableStyle} />
</MyStyledDiv>
)
}
Now what is "presumably" cool about this is that your style, is just an object now, and you can just import it and use it everywhere without makeStyles or withStyles, supposedly an advantage, even when to be honest, I have never used that of exporting/importing around; the code seems a bit cleaner nevertheless.
You seem to want to use it so all you do is.
export const myStyles {
// your styles here
}
because this object is equivalent in memory, and it is always the same object, something that is easier to mess up with styles, it should be as effective or even more than your hook, theoretically (if it re-renders often even when setup may be longer), which stores the same function in memory but returns a new object every time.
Now you can use those myStyles everywhere you deem reasonable, either with styled components or by assigning to sx.
You can further optimize, say if it's always a div that you use that is styled the same way, then the styled component MyStyledDiv should be faster, because it is the same and done each time. How much faster is this? According to some sources 55% faster, to me, it is taking 4 weeks of refactor and the JSS compatibility with emotion is bad, all mixed with SSR is making everything unusable and slow and broken, so let's see until then when the whole is refactored.
Here is a pattern that I've found useful in MUI 5. It allows you to keep style definitions in the same file but isolated, & avoids repeated function calls for every CSS property where you need to access your theme (e.g. width: ({ spacing }) => spacing(12))). It also feels similar to MUI's native CSS API.
Create a function that takes your theme as an argument & returns an object of named style groups. Then reference those groups directly in your sx props. This also allows for the use of classNames in a way similar to Material-UI 4.
import { useTheme } from '#mui/material';
import clsx from 'clsx';
export const NavItem = (props) => {
// Bring in style groups
const sx = styles(useTheme());
// Define classNames
const classNames = clsx({
isActive: props.isActive
});
return (
{/* Use classNames and style groups */}
<ListItemButton className={classNames} sx={sx.button}>
<ListItemAvatar sx={sx.avatar}>{props.icon}</ListItemAvatar>
<ListItemText>{props.label}</ListItemText>
</ListItemButton>
);
}
// Define style groups
function styles(theme) => {
return {
button: {
paddingX: 6,
'&.isActive': {
backgroundColor: theme.palette.secondary.light
}
},
avatar: {
'.isActive &': {
border: '2px solid green'
}
}
};
}
I'm in the same boat, about six months behind, i.e., starting to make the transition to v5 from v4 now... Just when I thought I had a handle on it all!
Having read this post and trying a few things out, I was able to replicate the ability to re-use a chunk of css. I'm a big fan of what used to be the overrides prop; that feature hasn't gone away, it's just under a different prop (loosely speaking). Regardless, I mention it because it provides access to what I like a lot about css: selectors.
To hit all MUI-Drawers my pref is for whatever the new overrides is. For targeted reuse of css I like the following:
import { reuseThisCss } from 'sharedCss';
export default styled(Drawer)(({ theme, ownerState }) => {
...
return {
'& .MuiDrawer-paper': {
boxShadow: xxl,
border: 'none',
'& .MuiListItemText-root': reuseThisCss,
},
};
export default ThisSpecificDrawerVariant;
Note: The focus is not on using styled (It's not my goto approach).
The css in the return value is the equivalent to the following css: .MuiDrawer-paper .MuiListItemText-root {...}.
This says, "select all .MuiListItemText-root under the .MuiDrawer-paper parent. If I want to optimize the render, while increasing the dependency on a specific hierarchy, I'll specify/expand on the selector that much more with whatever lies between the .MuiDrawer-paper and MuiListItemText-root. For instance, in my case:
...
return {
'& .MuiDrawer-paper': {
boxShadow: xxl,
border: 'none',
'& > a > li > div > .MuiListItemText-root': reuseThisCss,
},
};
Finally, per a question in the comments, generally this will not prevent a nested application of the style. In my experience, marking each level with a className is useful. I only "mark" the element that signals the start of a new level. So, if it were Drawer in the above example, I would start the css selector with .MUI-Drawer.level-3. The rest of css remains the same.
I still have not figured out if whether setting the className dynamically remains a performant and sufficiently flexible goto... TBD.
If you are using makeStyles or withStyles to provide CSS class, you can follow the instruction below.
CSS overrides created by makeStyles
Tailwind provides good option to get value from config inside another config object
module.exports = {
theme: {
colors: {
primary: 'red'
},
extend: {
colors: theme => ({
secondary: theme('colors.primary') // will be red
})
}
}
}
But this doesn't work when I want to create classes using RGBA. I want to create something like
module.exports = {
theme: {
colors: {
primary: 'red'
},
extend: {
boxShadow: theme => ({
extra: "0 0 999px rgba(theme('colors.primary'), .25))"
}),
}
}
}
This will render 0 0 999px rgba(red, .25) which is not correct CSS value - you need to pass red as 255, 0, 0. But I want to use colors.primary as it was defined in my config
I know Tailwind has it's own build utils for converting colors like asRgba, withAlphaVariable. For example, when you use text-white Tailwind renders it as color: rgba(255,255,255,var(--tw-text-opacity));. But how can I use it?
So basically how can I achieve this - pass color key from my config into another property and get rendered it as RGB/RGBA?
Update: I want third square (TW) to work as others
DEMO
I think your idea was wrong, because tailwind colors is defined with HEX number, but you trying to use it into RGBA type, so I think you need to convert firstly if you want to make your way.
Anyway let us know your success :)
check this doc
when passing my color as props like this <List text="something" color="#84AB86" /> and using in the code className={'bg-[${color}] '} it does not render properly.
when looking at chrome dev tools color are added correctly like this bg-[#84AB86]
while putting the color manually without taking it from props, it does work correctly
after more testing it seems not possible either to do it like this
const color = "#84CC79"
className={`bg-[${color}]`}
any idea why
To use dynamic classes with JIT tailwind you either need to use safelist config key or create stub file where you list all your dynamic classes that you will use.
Config example:
module.exports = {
content: [
'./pages/**/*.{html,js}',
'./components/**/*.{html,js}',
],
safelist: [
'bg-red-500',
'text-3xl',
'lg:text-4xl',
]
// ...
}
Or make safelist.txt in your src folder, then add classes there just like so:
bg-[#84AB86]
bg-[#fffeee]
// etc..
And don't forget to include this safelist.txt file to your config content so tailwind could watch it.
Explanation from tailwind docs
If you are not using JIT, then you can use safelist option for PurgeCSS:
// tailwind.config.js
module.exports = {
purge: {
// Configure as you need
content: ['./src/**/*.html'],
// These options are passed through directly to PurgeCSS
options: {
// List your classes here, or you can even use RegExp
safelist: ['bg-red-500', 'px-4', /^text-/],
blocklist: [/^debug-/],
keyframes: true,
fontFace: true,
},
},
// ...
}
From the Tailwindcss documentation
Dynamic values Note that you still need to write purgeable HTML when
using arbitrary values, and your classes need to exist as complete
strings for Tailwind to detect them correctly.
Don't use string concatenation to create class names --> <div className={mt-[${size === 'lg' ? '22px' : '17px' }]}></div>
Do dynamically select a complete class name --> <div className={ size === 'lg' ? 'mt-[22px]' : 'mt-[17px]' }></div>
Tailwind doesn’t include any sort of client-side runtime, so class
names need to be statically extractable at build-time, and can’t
depend on any sort of arbitrary dynamic values that change on the
client. Use inline styles for these situations, or combine Tailwind
with a CSS-in-JS library like Emotion if it makes sense for your
project.
As mentioned above tailwind engine In order to render a custom class dynamicaly:
Does not like:
className={`bg-[${custom-color}]-100`}
It expects:
const customBgColorLight = 'bg-custom-color-100';
className={`${customBgColorLight} .....`}
For this to work properly you have to include the name of the class in the safelist:[] in your tailwind.config.js.
For tailwind v.3
/** #type {import('tailwindcss').Config} */
module.exports = {
content: ['./src/**/*.{js,jsx,ts,tsx}'],
safelist: [
'bg-custom-color-500', // your-custom-css-class
'text-custom-color-500',
'border-custom-color-500',
..... // other classes
'hover:bg-custom-color-500', // *** also include it with the selector if needed ***
.... // other classes
],
theme: {
extend: {
colors: {
'custom-color': { // you have to use quotes if key is not in camelCase format
100: '#d6d6d6',
500: '#5E8EA2',
..... //other variants
},
...... // other colors
So you can use it:
// if you want store the values to an object
const yourClassObj = {
customBgColor: 'bg-custom-color-500',
customBrdColor: 'border-custom-color-500',
customTxtColor: 'text-custom-color-500',
};
const { customBgColor, customBrdColor, customTxtColor } = yourClassObj;
<YourComponent
className={`mb-2 font-semibold py-2 px-4 rounded-lg
${ conditionGoesHere ? `${customBgColor} text-white cursor-default`
: `${customTxtColor} border ${customBrdColor}
bg-transparent hover:border-transparent
hover:${customBgColor} hover:text-white`
}`}
/>
An easy solution is to use the built in style property.
For example in React:
Dont Use:
className={`bg-[${color}]`}
Use Instead:
style={{
backgroundColor: color,
}}
I would like to ask if there is any way to use different themes with tailindcss.
Imagine your site has 2 themes - dark and light.
I have a button:
<button class="px-4 bg-blue over:bg-grey-100">Register</button>
This is for the ligth theme.
Is it possible to change the design of the button depending on the theme.
For example in the body i have class: "theme-light" or "theme-dark".
Is it possible to change the classes of the button to apply only for one of the themes.
Something like that theme-dark:bg-orange:
<button class="px-4 bg-blue over:bg-grey-100 theme-dark:bg-orange">Register</button>
If not is there any way to have different themes without writing custom css, or completely rewriting my html pages for the given theme.
If not what's the best way to have 2 or more themes?
Thank you.
It is achievable to have theme-dark and theme-light variants by creating pseudo-class variants. It is detaily described here: https://tailwindcss.com/docs/pseudo-class-variants/#creating-custom-variants
For example, to create theme-dark pseudo-class variant you can do something like this in your tailwind.config.js file:
// tailwind.config.js
const plugin = require('tailwindcss/plugin')
module.exports = {
plugins: [
plugin(function({ addVariant, e }) {
addVariant('theme-dark', ({ modifySelectors, separator }) => {
modifySelectors(({ className }) => {
return `.${e(`theme-dark${separator}${className}`)}:theme-dark`
})
})
})
]
}
You can learn more about this here as well: https://tailwindcss.com/docs/plugins/#adding-variants
However, to achieve what you want, you will need to have some javascript behavior that will have a state from which will be able to tell when it is theme-light and when it is theme-dark, which means this is not achievable with 100% only tailwind.
Also Adam Wathan created quick them of this here: https://github.com/adamwathan/theming-tailwind-demo
I hope this answers your question.
The tw-colors plugin does what you want, the big advantage is that you don't need to use variants.
tailwind.config.js
const { createThemes } = require('tw-colors');
module.exports = {
content: ['./src/**/*.{astro,html,js,jsx,md,mdx,svelte,ts,tsx,vue}'],
plugins: [
createThemes({
halloween: {
'primary': 'orange',
'secondary': 'yellow',
},
summer: {
'primary': 'pink',
'secondary': 'red',
},
winter: {
'primary': 'blue',
'secondary': 'green',
},
party: {
'primary': 'steelblue',
'secondary': 'darkblue',
},
})
],
};
use themes like this with class:
<html class='theme-halloween'>
...
</html>
Or with data attributes:
<html data-theme='halloween'>
...
</html>
Themes can be switched dynamically with some toggle button or whatever you prefer
I've noticed that :hover in Tailwindcss uses the defaults hover selector which causes 'stuck' hover states on mobile. Is there a way to modify the :hover function to do a #media(hover:hover) instead?
update: There is now a better way. See this answer by Javier Gonzalez
original:
By far the simplest way is to add your own #media rule to the #responsive-class of rules in tailwind. How you can do that is described in the official tailwind documentation under the topic of custom media queries.
Simply add this to your config:
// tailwind.config.js
module.exports = {
theme: {
extend: {
screens: {
'hover-hover': {'raw': '(hover: hover)'},
}
}
}
}
This translates to #media (hover: hover) { ... } in css. And voila, you could use hover-hover:text-red to display red text only for devices that have hover ability.
To make your own, leave 'raw' as is and change the other two attributes to whatever media query you want. The first attribute hover-hover is what you use in tailwind. The second (hover: hover) is what your actual css #media query looks like. E.g.: hover: none or pointer: coarse.
Now, go ahead and use hover-hover:hover:text-red to modify your hover states.
Might be a bit late but the Tailwind team is already addressing this issue in Tailwind version 3 using a feature flag: https://github.com/tailwindlabs/tailwindcss/pull/8394
Once a new version is published with these changes Starting on tailwindcss v3.1.0, you could include a feature flag in your configuration to look like:
// tailwind.config.js
module.exports = {
future: {
hoverOnlyWhenSupported: true,
},
// ...
}
Yes, just generate the hover variant using a plugin that, besides adding the :hover pseudo-selector, also wraps all of the rules inside an #media(hover:hover) rule:
// tailwind.config.js
const plugin = require('tailwindcss/plugin');
const hoverPlugin = plugin(function({ addVariant, e, postcss }) {
addVariant('hover', ({ container, separator }) => {
const hoverRule = postcss.atRule({ name: 'media', params: '(hover: hover)' });
hoverRule.append(container.nodes);
container.append(hoverRule);
hoverRule.walkRules(rule => {
rule.selector = `.${e(`hover${separator}${rule.selector.slice(1)}`)}:hover`
});
});
});
module.exports = {
plugins: [ hoverPlugin ],
}
The responsive attributes like sm: md: lg: will do those media query job for you. Refer example in the docs. If you dont want to use hover state in mobile device. specify with eg:- sm:hover:no-underline
You can easily create your own hover like below:
// styles.css
#variants hover {
.banana {
color: yellow;
}
}
Then use it like class='hover:banana'
Using arbitrary variants
<button
type="button"
class="
[#media(hover:hover)]:opacity-0
[#media(hover:hover){&:hover}]:opacity-100
">
<!-- ... -->
</button>