extending default theme chakra ui - reactjs

I want to set default borderColor to all Input components but it doesn't work
This is my theme.js file:
import { extendTheme } from "#chakra-ui/react";
const config = {
initialColorMode: "light",
useSystemColorMode: false,
};
const theme = extendTheme({
config,
components: {
Input: {
borderColor: "teal",
},
},
});
export default theme;

Input is a multi-part component. You can figure out if the component you're trying to customise is single-part or multi-part by going to the docs for that component and clicking the View theme source button at the top of the page:
How to customise the theme: Docs
How to customise single-part and multi-part components: Docs (especially, how to customise multi-part components)
So in your case you need do something like this:
index.js :
import * as React from "react";
import { render } from "react-dom";
import { ChakraProvider, extendTheme } from "#chakra-ui/react";
import App from "./App";
const Input = {
variants: {
filled: () => ({
field: {
borderColor: "teal"
}
})
}
};
const theme = extendTheme({
components: {
Input
}
});
const rootElement = document.getElementById("root");
render(
<ChakraProvider theme={theme}>
<App />
</ChakraProvider>,
rootElement
);
App.js :
import * as React from "react";
import { Input } from "#chakra-ui/react";
export default function App() {
return <Input placeholder="extra small size" variant="filled" />;
}

Related

Published styled-components UI library does not have access to extended theme types on the consumer side

I am creating UI library using styled-components. I am extending the DefaultTheme type to support custom themes that are needed for our use case. Both theme and theme definition is coming from a different internal package that I use. When working on the UI library components, it works correctly. The issue begins when we tried to use the theming on the consumer side.
The problem is that, it seems that types are not really extended to the consumer correctly, so without adding styled.d.ts file on the client side, it doesn't seem to understand the types of the theme. Theme get a type anyand it should be read asDefaultTheme type.
I wonder if there is any way to expose the extended types to consumer so they don't have to add this additional file on their side?
Is there anyone out there who had similar problem? Maybe you could share your findings?
Here is my setup:
Design System project:
// theme.ts
import tokens from 'my-external-package/variables.json';
import type { ThemeProps } from 'styled-components';
import type { MyThemeType } from 'my-external-package//theme';
const { light, dark } = tokens.color.theme;
export const lightTheme = {
color: light,
};
export const darkTheme = {
color: dark,
};
export const defaultTheme = lightTheme;
// styled.d.ts
import {} from 'styled-components';
import type { MyThemeType } from 'my-external-package//theme';
// extend theme
declare module 'styled-components' {
// eslint-disable-next-line #typescript-eslint/no-empty-interface
export interface DefaultTheme extends MyThemeType {}
}
// CustomThemeProvider.tsx
import React, { createContext, useState, ReactNode, useContext } from 'react';
import { ThemeProvider } from 'styled-components';
import { lightTheme, darkTheme } from './theme';
const themes = {
light: lightTheme,
dark: darkTheme,
};
type CustomThemeProviderProps = {
children: ReactNode;
defaultTheme?: keyof typeof themes;
};
const themeContext = createContext({ toggleTheme: () => {} });
const { Provider } = themeContext;
export const CustomThemeProvider = ({
children,
defaultTheme = 'light',
}: CustomThemeProviderProps) => {
const [currentTheme, setCurrentTheme] = useState(defaultTheme);
return (
<Provider
value={{
toggleTheme: () =>
setCurrentTheme((current) => current === 'light' ? 'dark' : 'light'),
}}
>
<ThemeProvider theme={themes[currentTheme]}>{children}</ThemeProvider>
</Provider>
);
};
// I also export hook over here so I can use it on the client side
export const useToggleTheme = () => {
const { toggleTheme } = useContext(themeContext);
return toggleTheme;
};
App consumer NextJs
//_app.tsx
import type { AppProps } from 'next/app';
import { CustomThemeProvider } from 'my-library-package/theming';
function MyApp({ Component, pageProps }: AppProps) {
return (
<CustomThemeProvider defaultTheme='light'>
<Component {...pageProps} />
</CustomThemeProvider>
);
}
export default MyApp;
// consumer_page.tsx
import type { NextPage } from 'next';
import { useCallback, useState } from 'react';
import styled from 'styled-components';
import tokens from 'my-external-package/variables.json';
import { useToggleTheme, Switch } from 'my-library-package';
const CustomComponent = styled.p`
color: ${({ theme }) => theme.color.feedback.success.foreground};
`;
const MyPage: NextPage = () => {
const toggleTheme = useToggleTheme();
return (
<>
<Switch onChange={toggleTheme}/>
<CustomComponent>This component have access to theme</CustomComponent>
</>
)
}
export default MyPage;
We are considering re-export utilities from styled-components with the right DefaultTheme and instruct consumers not to install styled-components
Instruct design-system consumers to create a styled.d.ts file to get the theme correctly populated.
Both of those seems rather painful. :(

Material UI - Custom Select icon with custom themes

I'm creating a custom Material UI theme for my React application :
import {createTheme, Theme, ThemeOptions, ThemeProvider, useMediaQuery} from "#mui/material";
import {Components} from '#mui/material/styles/components';
import {FunctionComponent, PropsWithChildren, useMemo} from 'react';
import {select} from './select';
import {darkColors, lightColors} from './colors';
const components: Components<Theme> = {
MuiSelect: select
}
const lightThemeOptions: ThemeOptions = {
palette: lightColors,
components
};
export const lightTheme = createTheme(lightThemeOptions);
const darkThemeOptions: ThemeOptions = {
palette: darkColors,
components
};
export const darkTheme = createTheme(darkThemeOptions);
// eslint-disable-next-line #typescript-eslint/no-empty-interface
export interface SystemThemeProps {
}
export const SystemThemeProvider: FunctionComponent<PropsWithChildren<SystemThemeProps>> = ({children}) => {
const prefersDarkMode = useMediaQuery('(prefers-color-scheme: dark)');
const systemTheme = useMemo(() => prefersDarkMode ? darkTheme : lightTheme, [prefersDarkMode]);
return (
<ThemeProvider theme={systemTheme}>{children}</ThemeProvider>
);
};
And here is my select file :
import {Theme} from '#mui/material'
import {ComponentsOverrides} from '#mui/material/styles/overrides';
import {ComponentsProps} from '#mui/material/styles/props';
import {ComponentsVariants} from '#mui/material/styles/variants';
type MuiSelect = {
defaultProps?: ComponentsProps['MuiSelect'];
styleOverrides?: ComponentsOverrides<Theme>['MuiSelect'];
variants?: ComponentsVariants['MuiSelect'];
}
export const select: MuiSelect = {
styleOverrides: {
icon: ({ownerState, theme}) => ({
color: 'red'
})
}
}
So with that code, I can see the select native icon displayed with red color. So I think I've targeted the right classes to change.
However I have no idea how I can say that I don't want to use the native css but instead I want to load a JPEG file.
I guess I'll have one more class to target for the "open" state of the select.
Can you help ?

Storybook in React project display keys of the translations, not the values

I'm using "#storybook/react": "~6.3.12" and "react-intl": "^2.8.0" in my React project.
However, I'm stuck with a problem, that I'm not getting translation values of keys in JSON files. Already tried "storybook-addon-intl": "^2.4.1" and "storybook-react-intl": "^0.9.2".
preview.js looks like this:
import React from 'react';
import theme from '../src/theme';
import StoryRouter from 'storybook-react-router';
import { addDecorator, configure } from '#storybook/react';
import { Provider } from 'react-redux';
import configureStore from '../src/configureStore';
import { MuiThemeProvider } from '#material-ui/core/styles';
import { MuiPickersUtilsProvider } from '#material-ui/pickers';
import DateFnsUtils from '#date-io/date-fns';
import { addLocaleData, IntlProvider } from 'react-intl';
import { setIntlConfig, withIntl } from 'storybook-addon-intl';
import enLocaleData from 'react-intl/locale-data/en';
import deLocaleData from 'react-intl/locale-data/de';
import en from '../src/messages/en.json';
import {reactIntl} from './reactIntl.js';
// Global Redux/Intl Provider
const store = configureStore();
const withProvider = (story) => (
<Provider store={store}>
<MuiThemeProvider theme={theme}>
<MuiPickersUtilsProvider utils={DateFnsUtils} >
<IntlProvider
//locale='en' messages={en}
>
{story()}
</IntlProvider>
</MuiPickersUtilsProvider>
</MuiThemeProvider>
</Provider>
);
addDecorator(withProvider);
addDecorator(StoryRouter());
// addLocaleData(enLocaleData);
// addLocaleData(deLocaleData);
// const messages = {
// 'en': require('../src/messages/en.json'),
// 'de': require('../src/messages/de.json')
// };
// const getMessages = (locale) => messages[locale];
// setIntlConfig({
// locales: ['en', 'de'],
// defaultLocale: 'en',
// getMessages,
// });
// addDecorator(withIntl);
const req = require.context('../src/stories', true, /\.stories\.js$/)
function loadStories() {
req.keys().forEach((filename) => req(filename))
}
configure(loadStories, module);
export const parameters = {
actions: { argTypesRegex: "^on[A-Z].*" },
controls: {
matchers: {
color: /(background|color)$/i,
date: /Date$/,
},
},
docs: {
// opt-out of inline rendering
inlineStories: false,
}
}
and sample component like this
login.stories.js
import React from 'react';
import { storiesOf } from '#storybook/react';
import Login from '../../components/login/index';
import SmsVerificationDialog from '../../components/login/smsVerification/SmsVerificationDialog';
export default {
title: 'Login/Login',
component: Login,
};
storiesOf('Login/Login', module)
.add('Login Page', () => (
<Login />
))
.add('Sms Verification Dialog', () => (
<SmsVerificationDialog open={true} />
))
So output I have look like this
login.dialog.title
And I need to see "Login to your account"
What might cause the issue and how to solve it? Might there be a solution without addons because I opened issues in GitHub repos and got no answers at all, seems like those repos are abandoned.

TypeError: Cannot read property 'primaryColor' of undefined - Typescript, Styled Components (tsx)

I am running a few random react-library tests here and I keep running into the following TypeError: Cannot read property 'primaryColor' of undefined. The component renders. But I seem to be missing something. I have been scratching my head for hours on this one to no avail as is believe the types have already been set. I have included the relevant code below. Can you help me spot the issue? A 1000 thank yous in advance for your help.
TypeError: Cannot read property 'primaryColor' of undefined
22 | props.isBackgroundPrimary2Color
23 | ? "white"
> 24 | : props.theme.colors.primaryColor}
| ^
25 | 0%,
26 | white 190%
27 | );
import React from "react";
import styled from "styled-components";
interface PrimaryButtonProps {
children: string;
//note boolean props start with is to convey a question
isBackgroundPrimary2Color?: boolean;
}
//shortened this section for brevity
const PrimaryButtonStyles = styled.button<PrimaryButtonProps>`
background: linear-gradient(
45deg,
${(props) =>
props.isBackgroundPrimary2Color
? "white"
: props.theme.colors.primaryColor}
0%,
white 190%
);
`
const PrimaryButton = ({
isBackgroundPrimary2Color,
children,
...otherProps
}: PrimaryButtonProps) => {
return (
<PrimaryButtonStyles
isBackgroundPrimary2Color={isBackgroundPrimary2Color}
{...otherProps}
>
{children}
</PrimaryButtonStyles>
);
};
export default PrimaryButton;
As per the documentation here is my styled.d.ts file:
import "styled-components";
// extend the default theme using declaration merging theme into styled components
declare module "styled-components" {
export interface DefaultTheme {
fonts: {
family: string;
size: {
standard: string;
};
fontColors: {
primaryDisabledButtonFont: string;
primaryButton2Font: string;
};
};
colors: {
background: string;
primaryColor: string;
};
}
}
Here is my theme.tsx file:
import React from "react";
import { DefaultTheme } from "styled-components";
const theme: DefaultTheme = {
fonts: {
family: "sans-serif",
size: {
standard: "16px",
},
fontColors: {
primaryDisabledButtonFont: "#0000004d",
primaryButton2Font: "#64E2D3",
},
},
colors: {
background: "#f8f8f8",
primaryColor: "#645f43",
},
};
export { theme };
And finally the App.tsx file and index.tsx file:
import React, { useState } from "react";
import NavBar from "./components/NavBar/NavBar";
import Grid from "./layout/Grid";
import { Footer } from "./components/Footer/Footer";
import PrimaryButton from "./components/Buttons/PrimaryButton";
function App() {
return (
<div>
<PrimaryButton>Sign Up</PrimaryButton>
</div>
);
}
export default App;
and lastly my index.tsx file
import React from "react";
import ReactDOM from "react-dom";
import { ThemeProvider } from "styled-components";
// import "./index.css"; removed and moved to globalStyles
import App from "./App";
import { theme } from "./styles/theme";
import GlobalStyle from "./styles/GlobalStyles";
ReactDOM.render(
<React.StrictMode>
<ThemeProvider theme={theme}>
<GlobalStyle />
<App />
</ThemeProvider>
</React.StrictMode>,
document.getElementById("root")
);
What am I missing?
It's throwing that error because you probably did not add the theme decorator in your .preview file. This is how I solved this problem.
I created decorator.ts file with my theme decorator like this
import React from 'react'
import { DecoratorFn } from '#storybook/react'
import { ThemeProvider } from 'styled-components'
import { lightTheme, darkTheme } from '../styles/theme'
import { GlobalStyle } from '../styles/GlobalStyle'
const withTheme: DecoratorFn = (StoryFn, context) => {
return (
<ThemeProvider theme={lightTheme}>
<GlobalStyle />
<StoryFn />
</ThemeProvider>
)
}
export const globaDecorators = [withTheme]
Then inside the .preview.ts file, you can export the theme decorator like this
export const decorators = globaDecorators
This will work. You will have to restart your storybook dev server for it work

How to change a SASS variable value using React Js?

Before making it duplicate question please make sure you read my question
I am asking this question in 2019, Where React Js documentation specify we can use SASS in our react project here's link
I want to switch between light theme and dark theme using dynamic variable which is control by user click
My React Code
import React from 'react';
import './App.scss';
class App extends React.Component {
render() {
return (
<div className="Home">
I’m slow and smooth
<button onClick={() => console.log()}>Change theme</button>
</div>
);
}
}
export default App;
My SASS code:
$theme: light; // I want to control this variable
$bgcolor: #222222;
#if($theme== light) {
$bgcolor: white;
}
#else {
$bgcolor: black;
}
.Home {
background-color: $bgcolor;
height: 100vh;
}
in case you are still interested, you can kind of change a sass variable by instead using css variables
:root {
--app-primaryColor: #f49ad1;
--app-secondaryColor: #211f1e;
}
Then use these variables in your scss files
.button {
background-color: var(--app-primaryColor);
color: var(--app-secondaryColor);
}
and update them using React
document.documentElement.style.setProperty('--app-primaryColor', '#ffae00')
Here is a (almost) full example using react and redux. A setTheme action is used to update colors from the document root element. This way you can also configure your theme directly from your react root tag props. These props will be set as the initial state.
// index.html
<div
id="app"
primaryColor="red"
secondaryColor="#f2f2f2"
/>
// css-variables.scss
:root {
--app-primaryColor: #f49ad1;
--app-secondaryColor: #211f1e;
}
// app.module.scss
.button {
background-color: var(--app-primaryColor);
color: var(--app-secondaryColor);
}
// index.js
import React from 'react'
import ReactDOM from 'react-dom'
import { Provider } from 'react-redux'
import './css-variables'
import App from './app'
import configureStore from './configureStore'
const rootElement = document.getElementById('app')
//Here you could extract your theme variables from your rootElement props and set it as an initial redux state
const initialProps = {}
const store = configureStore(initialProps)
ReactDOM.render(
<Provider store={store}>
<App />
</Provider>,
rootElement)
// app.js
import React from 'react'
import { connect } from 'react-redux'
import { setTheme } from './actions'
import styles from './app.module'
class App extends React.Component {
componentDidMount() {
//In case you have an initial state set from your rootElement
const { theme, setTheme } = this.props
setTheme(theme)
}
generateTheme() {
const primaryColors = ['#ffae00', '#f49ad1', '#d0666b', '#7c6cd0', '#6cd09d', '#d0ba6c']
const secondaryColors = ['#4c4c4e', '#2f2f2f', '#dcdcdc', '#fff']
return {
primaryColor: primaryColors[Math.floor(Math.random() * primaryColors.length)]
secondaryColor: secondaryColors[Math.floor(Math.random() * secondaryColors.length)]
}
}
onButtonClick() {
const theme = this.generateTheme()
this.props.setTheme(theme)
}
render() {
return (
<div className="{styles.button}" onClick={this.onButtonClick.bind(this)}>
Change theme
</div>
)
}
}
const mapStateToProps = (state) => ({
theme: state.theme,
})
export default connect(mapStateToProps, { setTheme })(App)
// actions.js
export const setTheme = theme => dispatch => {
//You change your theme vars directly from the root element
Object.keys(theme)
.filter(prop => typeof theme[prop] !== 'undefined' && theme[prop] !== null)
.forEach(prop => document.documentElement.style.setProperty(`--app-${prop}`, theme[prop]))
dispatch({
type: 'THEME/SET',
payload: theme
})
}

Resources