I am creating a custom UI library for work that uses Material-UI. The UI library has a custom theme where I added to the palette object with custom company colors. The theme lives in the UI library, and for elements that live there, I can use the custom colors in makeStyles, but when I try to use the exported theme in the main codebase, the custom colors throw errors in makeStyles.
I believe the issue is I'm not exporting the theme's custom module. I am unsure how to export and import this correctly into the main codebase. Currently, I am only exporting the theme file and importing it into the main codebase.
Main Codebase:
import { theme } from 'customUILibrary';
...
<MuiThemeProvider theme={theme}>
...
</MuiThemeProvider>
CustomUILibrary:
index.ts:
export { theme } from 'theme/theme';
theme/theme.ts
import {
createTheme,
responsiveFontSizes,
} from '#material-ui/core/styles';
import './extendPalette';
export const theme = responsiveFontSizes(createTheme({
palette: {
gradients: {
primary: 'linear-gradient(270deg, #35C7E1 -10.76%, #1A92BD 121.8%)',
secondary: 'linear-gradient(270deg, #194E94 0%, #317CA6 100%)',
},
},
}));
extendPalette.ts
declare module '#material-ui/core/styles/createPalette' {
export interface PaletteOptions {
gradients: {
primary: string
secondary: string,
},
}
export interface Palette {
gradients: {
primary: string
secondary: string,
},
}
}
The custom theme attributes work great inside the UI Library with the UI elements, but when imported into the main codebase the custom attributes aren't being picked up, and in fact causing errors.
As suggested here, you can do something like this:
declare module "#mui/material/styles" {
interface Palette {
custom: {
pink: string;
};
}
interface PaletteOptions {
custom: {
pink: string;
};
}
}
Then you can set you value in theme safely:
const theme = createTheme({
palette: {
custom: {
pink: "pink"
}
}
});
and use it in styles:
<Button sx={{ color: "custom.pink" }}>Content</Button>
See my codesandbox example
Related
My Problem
I have a project which requires icons everywhere. Instead of rendering a Fontawesome Icon in every script, I have a functional component which renders an icon when given props.
When calling the function, sometimes it doesn't accept the color prop. Only certain colors seem to be working, such as darkBlue, lightBlue, and green. Colors which haven't accepted the prop are defaulting to white.
I'm using Tailwindcss to inject classes into the components.
Tailwind Config
module.exports = {
content: ["./src/**/*.{js,jsx,ts,tsx}"],
theme: {
colors: {
dark: "#121212",
white: "#fff",
secondary: "#F0A500",
lightBlue: "#0EA5E9",
darkBlue: "#2563EB",
beige: "#FDBA74",
silver: "#9CA3AF",
red: "#DC2626",
green: "#10B981",
orange: "#F97316",
hotPink: "#EC4899",
purple: "#6D28D9",
yellow: "#FDE047",
},
extend: {
},
},
plugins: [],
};
FC: Icon Render
import React from "react";
import { FontAwesomeIcon } from "#fortawesome/react-fontawesome";
// color props must be passed as a string
function Icon({ name, color, scale }) {
return (
<FontAwesomeIcon
icon={name}
className={`text-${color}`}
size={scale}
/>
);
}
export default Icon;
Calling Icon Render
import React from "react";
import Button from "../../shared/components/Button";
import Typography from "../../shared/utilities/Typography";
import Mugshot from "../../shared/assets/mugshot.jpg";
import Icon from "../../shared/components/Icon";
import {
faGlobe,
faHandSpock,
faComment,
} from "#fortawesome/free-solid-svg-icons";
import Avatar from "../../shared/components/Avatar";
function example() {
return (
<section className="section" id="home-hero">
<Typography variant="label">Some text</Typography>
<Typography variant="h2">
Some text <Icon name={faHandSpock} color="beige" />
</Typography>
</section>
);
}
export default example;
What I've Tried / Fun Facts
No errors in the console.
Some colors may be preserved tailwind color names?
Tried changing color names in tailwind config
Tried changing hex values in tailwind config
Conclusion
Edit: Discovered an easier way:
<Icon name={faHandSpock} color="text-beige" /> // full classname
// remove partial className, pass in object
function Icon({ name, color, scale }) {
return (
<FontAwesomeIcon
icon={name}
className={color}
size={scale}
/>
);
}
export default Icon;
TailwindCSS doesn't allow you to generate classes dynamically. So when you use the following to generate the class…
className={`text-${color}`}
…TailwindCSS will not pick that up as a valid TailwindCSS class and therefore will not produce the necessary CSS.
Instead, you must include the full name of the class in your source code.
For this you can create any function which returns the required string like this:
function changeFAColor (color) {
if(color === dark) return "text-dark"
(color === white) return "text-white"
(color === secondary) return "text-secondary")
.
.
.
(color === purple) return "text-purple")
(color === yellow) return "text-yellow")
}
And use it in the component
<FontAwesomeIcon
icon={name}
className={`${changeFAcolor(color)}`}
size={scale}
/>
Tailwind generates a CSS file which contains only the classes that you've used in the project.
The problem you're experiencing is because Tailwind doesn't recognise the generated class you're applying in "FC: Icon Render". In particular, this line:
className={`text-${color}`}
To quote the documentation:
The most important implication of how Tailwind extracts class names is
that it will only find classes that exist as complete unbroken strings
in your source files.
If you use string interpolation or concatenate partial class names
together, Tailwind will not find them and therefore will not generate
the corresponding CSS:
https://tailwindcss.com/docs/content-configuration#class-detection-in-depth
To resolve your problem, either pass in the full class name instead of generating it or safelist all of your text-{color} classes in your config file.
Assign your colors to a variable:
const colors = {
dark: "#121212",
white: "#fff",
...
Pass them into your config for theme:
theme: {
colors,
. . .
Safelist your colors:
safelist: Object.keys(colors).map(color => `text-${color}`),
I set the default theme on Chakra UI for React with extendTheme().
When I try to change the Select component's style like this, the _focus style does not applied, while the color behaves as expected.
Refs
Chakra-UI Style Props
How to change the placeholder color globally?
Captions
Codes
index.ts
import { extendTheme } from '#chakra-ui/react';
import { Select } from './select';
export const theme = extendTheme({
colors: {
brand: '#008561',
brandLight: '#00b485',
},
components: {
Select,
},
});
select.ts
export const Select = {
parts: ['field'],
baseStyle: ({ colorMode }: any) => ({
field: {
color: 'brandLight',
_focus: {
borderColor: colorMode === 'dark' ? 'brandLight' : 'brand',
},
},
}),
sizes: {},
variants: {},
defaultProps: {},
};
To change the focus border color, you have to access the focusBorderColor selector,
This can either be set in the variant you want to change, or in the defaultProps selector of your component theme.
defaultProps: {
focusBorderColor: 'gray.500',
},
Another workaround I have seen, is to set the global shadows to a chakra color, note that any color you define, can also be accessed like the example below
const colors = { mycolor: "#F39C12" }
const shadows = {
outline: '0 0 0 3px var(--chakra-colors-mycolor-500)',
};
const theme = extendTheme({ config, colors, styles, components, shadows });
Workaround was found here: Chakra-UI Issue-663
Hope this helps your project
This worked for me.
_focusVisible={{
outline: "none",
}}
I am a beginner in material react. I used a palette in the theme in my code, but it shows me a different color code when it displays. please guide me
theme code
import {createMuiTheme} from "#material-ui/core";
const primaryColor="#FF4D23";
const Theme = createMuiTheme({
overrides: {
MuiTypography: {
root: {
fontFamily: "shabnam !important",
}
},
palette:{
primary:{
main:primaryColor,
}
}
}
});
export default Theme;
style code
LogoText:{
color:theme.palette.primary.main,
fontSize:'23px ',
fontWeight:600,
marginRight:'10px'
},
But it shows
**
color: #3f51b5;
**
I am using react-native-elements in my react-native application.
My app is wrapped with the ThemeProvider to pass the theme down to all components.
<SafeAreaProvider>
<ThemeProvider theme={Theme}>
<Loader visible={loader.loading} text={loader.message} absolute={true} />
<RootNavigation />
</ThemeProvider>
</SafeAreaProvider>
In the theme file i define the values i want to use across the app.
const theme = {
colors: {
primary: '#6A69E2',
primaryDark: '#4747c2',
primaryLight: 'rgba(106, 105, 226, 0.35)',
gray: {
dark: '#242424',
default: '#666',
medium: '#999',
light: '#ccc',
lightest: '#e7e7e7',
},
},
text: {
size: {
small: 12,
default: 16,
large: 18,
h1: 26,
h2: 22,
h3: 20,
},
},
Text: {
style: {
fontSize: 16,
color: '#242424',
fontFamily: 'Roboto',
},
},
Button: {
style: {
borderRadius: 50,
},
disabledStyle: {
backgroundColor: 'rgba(106, 105, 226, 0.35)',
},
},
};
export default theme;
For the values the original theme of react-native-elements providing this is working. For example i can access the colors by using
const theme = useTheme()
theme.colors.primary
But when i want to add some new properties like primaryDark i'll get an linter error.
Object literal may only specify known properties, and 'primaryDark' does not exist in type 'RecursivePartial<Colors>'.ts(2322)
In the doc of react-native-elements is a part about declaration merging, but i don't understand how i can archive this
https://reactnativeelements.com/docs/customization/#typescript-definitions-extending-the-default-theme.
Somebody could help me with this?
Well, declaration merging still works. This seems like a bug on the lib's part.
Their doc says you can augment the Color interface in module 'react-native-elements'. But currently (as of 2021-04-18, with v3.3.2) that interface is actually hidden inside module 'react-native-elements/dist/config/colors', not directly exposed at the top level, weird.
I suggest you file an issue to their repo. Never mind, someone already filed the issue.
Tested on my machine, following solution works.
import React from 'react'
import { useTheme, ThemeProvider } from 'react-native-elements'
declare module 'react-native-elements/dist/config/colors' {
export interface Colors {
primaryDark: string
primaryLight: string
}
}
const ChildComp = () => {
const theme = useTheme()
theme.theme.colors.primaryDark // <-- No more error 🎉
return <div>foobar</div>
}
Reply to OP's comment. You can augment interface however you like, as long as the augmented key doesn't exist before. For example add foobar key to FullTheme.
declare module 'react-native-elements' {
export interface FullTheme {
foobar: string
}
}
I have just started using Storybook for a UI component lib I am working on. I wanted to extract JSDoc written for JS class methods and properties into Storybook and create a Doc.
Storybook does support creating doc for React components by reading its propTypes. Is there addon or someway to do the same for a JS class.
I am using the latest storybook 6.
Thanks in advance
You can do it like a normal component:
form-validators.stories.ts
import { FormValidators } from './path';
export default {
title: 'Components/Form Validators',
component: FormValidators,
parameters: {
previewTabs: { canvas: { hidden: true } },
docsOnly: true,
},
} as Meta;
export const Default: Story = () => ({
template: '<div>Test</div>',
});
Or I prefer an MDX file.
form-validators.stories.mdx
import { ArgsTable } from '#storybook/addon-docs/blocks';
import { Meta } from '#storybook/addon-docs/blocks';
import { FormValidators } from './path';
<Meta
title="Components/Form Validators"
parameters={{ previewTabs: { canvas: { hidden: true } } }}
/>
<ArgsTable of={FormValidators} />