how to apply a few styles properties depends on condition styled-components - javascript

Is it possible to apply a few styles of properties at once?
const Button = styled.div`
color: blue;
opacity: 0.6;
background-color: #ccc;
`
I need to pass active property, which will affect color, opacity, background-color. How can I apply styles for the active button at once instead of declaring conditions for each property?
const Button = styled.div`
color: ${props.active ? 'green' : 'blue'};
opacity: ${props.active ? 1 : 0.6};
background-color: : ${props.active ? 'white' : '#ccc'};

Create two classes and switch that classes according to property active
For example -
CSS
.activeClass{
color: green;
opacity: 1 ;
background-color:white;
}
.inactiveClass{
color: blue;
opacity: 0.6;
background-color: #ccc;
}
in Render
<button id="mybtn" className={this.props.active ? 'activeClass' : 'inactiveClass'} >mybutton</button>
See working example here

A common approach is a conditional rendering of CSS blocks with css API:
const first = css`
color: green;
opacity: 1;
background-color: white;
`;
const second = css`
color: blue;
opacity: 0.6;
background-color: #ccc;
`;
const Button = styled.div`
${({ active }) => (active ? first : second)}
`;
const App = () => {
const [active, trigger] = useReducer(p => !p, false);
return (
<Button active={active} onClick={() => trigger()}>
Press Me
</Button>
);
};
Or using common utilities like swithProp from styled-tools:
import styled, { css } from "styled-components";
import { switchProp, prop } from "styled-tools";
const Button = styled.button`
font-size: ${switchProp(prop("size", "medium"), {
small: prop("theme.sizes.sm", "12px"),
medium: prop("theme.sizes.md", "16px"),
large: prop("theme.sizes.lg", "20px")
}, prop("theme.sizes.md", "16px"))};
${switchProp("theme.kind", {
light: css`
color: LightBlue;
`,
dark: css`
color: DarkBlue;
`
}, css`color: black;`)}
`;
<Button size="large" theme={{ kind: "light" }} />

Related

Can't Style Grommet icon inside Anchor

I'm new to Grommet with styled components.
I Have already checked all the docs and can't find the solution.
PROBLEM
I have an Anchor with an icon and a label.
Problem is I cannot target the icon for styling when i hover or it is active.
Text / Label changes the styling though. How can i achieve/fix this?
I've also tried using styled components and putting an Icon and a Text inside a Grommet Box, but didn't work.
Please help!
import React from "react";
import { Anchor, Box, Text } from "grommet";
import styled from "styled-components";
import { Currency as PayoutIcon, Menu as MenuIcon } from "grommet-icons";
const StyledAnchor = styled(Anchor)`
display: flex;
height: 56px;
color: #808191;
padding: px 20px;
border-radius: 12px;
background: transparent;
width: max-content;
text-decoration: none;
font-family: Inter;
color: #808191;
padding: 0px 20px;
background: transparent;
transition: all 0.25s ease 0s;
text-decoration: none;
border: none;
&:visited {
text-decoration: none;
border: none;
}
&:hover {
color: #6c5dd3;
text-decoration: none;
}
&:active {
color: #fff;
background: #6c5dd3;
text-decoration: none;
border: none;
}
&:focus {
color: #fff;
background: #6c5dd3;
textdecoration: none;
border: none;
}
`;
const SidebarItem = () => {
return (
// <Box color="#808191" hoverIndicator="true">
<StyledAnchor
color="#808191"
label="Payouts"
onClick={() => {}}
href="#"
icon={<PayoutIcon />}
/>
// </Box>
);
};
export default SidebarItem;
For the granularity of styles you are looking for, I think you can directly use the Button component instead of Anchor, nevertheless, the usage of Button is more compliant with accessibility standards (WCAG) for the Sidebar interactive elements that you are describing above.
Grommet works best with styled-components, yet grommet theme-ing is also very powerful, and knowing how to leverage its capabilities will help you use styled-components much less.
Recently, grommet extended the Button theme (kind/default button), and that should do the trick for you with no sweat and no need for styled-components, here is an example:
import React, { useState } from "react";
import { render } from "react-dom";
import { Box, Grommet, Button } from "grommet";
import { Currency as PayoutIcon } from "grommet-icons";
const theme = {
global: {
colors: {
myColor: "#808191",
"background-contrast": {
dark: "#FFFFFF14",
light: "#0000000A"
},
"active-background": "background-contrast",
"active-text": "red",
icon: "text",
// focus color is an important indication for keyboard navigation accessibility,
// it will be an ill advice to set it to undefined and remove focus
focus: "teal",
text: {
dark: "#C0CADC",
light: "#444444"
}
}
},
button: {
default: {
color: "#808191",
border: undefined,
font: {
weight: 700
},
padding: {
horizontal: "12px",
vertical: "6px"
}
},
hover: {
default: {
background: {
color: "background-contrast"
},
color: "brand"
},
secondary: {
border: {
width: "3px"
},
padding: {
horizontal: "9px",
vertical: "3px"
}
}
},
active: {
background: {
color: "aliceblue"
},
color: "teal",
secondary: {
border: {
color: "transparent"
}
}
}
}
};
const SidebarItem = () => {
const [active, setActive] = useState();
return (
<Button
active={active}
label="Payouts"
icon={<PayoutIcon />}
onClick={() => {
setActive(!active);
}}
href="#"
/>
);
};
export const App = () => {
return (
<Grommet theme={theme}>
<Box pad="small" align="start">
<SidebarItem />
</Box>
</Grommet>
);
};
render(<App />, document.getElementById("root"));
Here is a codesandbox for running it live.
The Button has the granularity for active/hover/disabled and more, you can basically gain the same functionality in Anchor using the theme anchor.extend but this way is a much cleaner approach.

Trying to pass 'active' class styles to a nested SVG that's passed as a prop to button element

I have a Menu/Tabs React component I'm building that I've set up to receive a combination of both an SVG and a string of text as the title prop in order to display as the tab name.
The issue I'm having is that I have an active class that colors the active tab text blue, but not the SVG which remains black.
Desired Behavior: Have active class CSS stylings applied to both text and SVG elements so that they are both blue when active and both black when they are inactive
Current Behavior: Active class stylings are applied to the text but not the SVG. When tab is active text turns blue, while the SVG remains black
Here's a CodeSandbox demonstrating the problem:
My Current Tabs Component:
// #ts-nocheck
import React, { useState } from 'react';
import styled from '../../styles/styled';
type Props = {
children: React.ReactNode;
};
export const Tabs = (props: Props) => {
const { children } = props;
const [tab, setTab] = useState(0);
const childrenList = React.Children.toArray(children);
const tabs = childrenList.map((child, idx) => {
const title = (child as any).props.title ?? idx;
return (
<StyledTabs key={title} className={tab === idx ? 'activeTab' : ''} onClick={() => setTab(idx)}>
{title}
</StyledTabs>
);
});
const current = childrenList[tab];
return (
<div>
<div>{tabs}</div>
<div>{current}</div>
</div>
);
};
const StyledTabs = styled.button`
margin: 0 10px;
padding: 0 10px;
padding-bottom: 5px;
border: none;
background: transparent;
display: inline-block;
font-weight: 700;
text-transform: uppercase;
&.activeTab {
color: #1471da;
border-bottom: 1px solid #1471da;
outline: none;
padding-bottom: 5px;
}
`;
Page where Tabs Component is used :
const OverviewIcon = () => (
<svg xmlns="http://www.w3.org/2000/svg" width="35" height="35">
<path d="M0 0h16v16H0zM19 0h16v16H19zM0 19h16v16H0zM19 19h16v16H19z" />
</svg>
);
const OverviewTab = () => (
<>
<OverviewIcon />
<span>OVERVIEW</span>
</>
);
...
<Tabs>
<div title={<OverviewTab />}>
<ContentSection></ContentSection>
</div>
<div title={'ADDITIONAL CONTACTS'}>
<h1>CONTACTS</h1>
</div>
</Tabs>
To change an svg color, use fill instead within your scss :
&.activeTab {
color: #1471da;
fill: #1471da;
border-bottom: 1px solid #1471da;
outline: none;
padding-bottom: 5px;
}

Add styles to button when is active and remove them when clicked again

Im working with react and Draft.js in a plataform with meetings, forums, etc. This editor allows the user to take notes during the meetings. What i want to achieve is that when the user clicks 'Italic' add the .active class to let know the user that the button is active (change the background color) And remove it when is not. This is my code:
export const ActionButton = styled.div`
color: #272a2d;
padding: 3px 7px;
margin-top: 13px;
.active {
background-color: pink
}
&:hover {
background-color: #f2f4f6;
border-radius: 8px;
cursor: pointer;
}
.icon-toolbar-custom-icons {
border: none;
border-radius: 8px;
padding: 5px;
&:hover {
background-color: #f2f4f6;
}
${mediaQuery} {
padding: 0;
}
}
`;
const estilosTooltipInfo = makeStyles(theme => ({
arrow: {
color: theme.palette.common.black,
},
tooltip: {
backgroundColor: theme.palette.common.black,
fontSize: '13px',
padding: '8px 10px',
borderRadius: 6,
},
}));
function TooltipInfo(props) {
const classes = estilosTooltipInfo();
return <Tooltip placement="bottom" classes={classes} {...props} />;
}
function TooltipItalic(props) {
const handleSetItalic = () => {
const newState = RichUtils.toggleInlineStyle(props.editorState, 'ITALIC');
if (newState) {
props.onChange(newState);
}
};
return (
<div>
<TooltipInfo title="Cursiva">
<ActionButton
className="icon-toolbar-custom-icons"
onClick={handleSetItalic}
>
<FormatItalicIcon />
</ActionButton>
</TooltipInfo>
</div>
);
}
i don't know how to achieve this in my onClick method. I know it should be easy but i'm having a hard time here.
It's pretty easy. What you need is getCurrentLinlineStyle of EditorState. It holds current (that you've selected) inline styles. You could see the link with the example below.
Codesanbox example
use onMouseDown instead of onclick event will do that,
export default function MyEditor() {
const [editorState, setEditorState] = React.useState(
() => EditorState.createEmpty(),
);
const _onBoldClick = () => {
setEditorState(RichUtils.toggleInlineStyle(editorState, 'BOLD'))
}
return(
<div>
<button
// onClick={_onBoldClick}
onMouseDown={e=> {
e.preventDefault();
setEditorState(RichUtils.toggleInlineStyle(editorState, 'BOLD'))
}}>BOLD</button>
<div
>
<Editor
textAlignment="left" placeholder="Enter something here"
editorState={editorState} onChange={setEditorState} />
</div>
</div>
)
}

Styled Components - Conditionally render an entire css block based on props

I understand that styles can be conditionally rendered such as:
const HelloWorldLabel= styled("div")<{ centered?: boolean }>`
display: ${({ centered }) => (centered ? "block" : "flex")};;
margin: ${({ centered }) => (centered ? "auto 0" : "unset")};
padding: ${({ centered }) => (centered ? "0 15px" : "unset")};
`;
This does not look DRY - How can I (is it possible) render an entire block of css styles based on props?
Something like:
const HelloWorldLabel= styled("div")<{ centered?: boolean }>`
if (centered) {
display: "block" ;
margin: $"auto 0";
padding: "0 15px" ;
} else {
......
}
`;
With styled-component, or any CSS-in-JS, you can conditionally render a css block:
import styled, { css } from 'styled-components';
const light = css`
background-color: white;
color: black;
`;
const dark = css`
background-color: black;
color: white;
`;
const Box = styled.div`
${({ isDark }) => (isDark ? light : dark)}
`;
Full Example:
import React, { useState } from 'react';
import ReactDOM from 'react-dom';
import styled, { css } from 'styled-components';
const light = css`
background-color: white;
border: 2px solid black;
color: black;
`;
const dark = css`
background-color: black;
color: white;
`;
const FlexBox = styled.div`
margin: 20px;
padding: 20px;
${({ isDark }) => (isDark ? light : dark)}
`;
const App = () => {
const [isDark, setIsDark] = useState(false);
const toggle = () => setIsDark(b => !b);
return (
<FlexBox isDark={isDark}>
<div>Some Text</div>
<button onClick={toggle}>Change Block</button>
</FlexBox>
);
};
ReactDOM.render(<App />, document.getElementById('root'));
A less verbose way that worked for me is
const SideMenu = styled.aside`
width: 200px;
${({ isHidden }) => isHidden && `
display: none;
`}
// another random prop you need here
${({ redBg }) => redBg && `
background-color: red;
`}
`;
You can use a function and return the css based on prop:
const HelloWorldLabel= styled("div")`
${({centered}) => {
if (centered) {
return `
display: "block" ;
margin: "auto 0";
padding: "0 15px";
`
} else {
return `// Other styles here`
}
}}
`;
The alternative is
let customCss = setCustomCss(position) => {
let positionCss = {
center: [ 'css: value;', 'css:value;'],
left: .....
right: ....
}
return return positionCss[position];
}
let HelloWorldLabel= styled('div')(customCss, {
/* css common to all */
})

Create new component and inherit styles from styled-component

const Button = styled.button`
display: inline-block;
width: 300px;
background-color: black;
`
const ButtonHref = styled.a`
${Button}
`
So I have two styled-components. I want to inherit 'Button' styles but create another tag. I use react-emotion. How can I do this?
There are a few options here, using composition, styled components, or with props. The second option is probably what you want, but I've provided two other options as well.
1. Using composition
const baseButton = css`
color: white;
background-color: black;
`
const fancyButton = css`
background-color: red;
`
render() {
return (
<div>
<button css={baseButton}></button>
<button css={[baseButton, fancyButton]}></button>
</div>
)
}
The second button will have the baseButton and specialButton styles.
Or...
const baseButton = css`
color: white;
background-color: black;
`
const fancyButton = css`
${baseButton};
background-color: red;
`
render() {
return (
<div>
<button css={baseButton}></button>
<button css={fancyButton}></button>
</div>
)
}
2. Using styled components
const Button = styled.button`
color: white;
background-color: black;
`
const Fancy = styled(Button)`
background-color: red;
`
render() {
return (
<div>
<Button>Button</Button>
<Fancy>Fancy</Fancy>
</div>
)
}
This works for any component that accepts the className prop, which button does.
3. Using props
const Button = styled.button`
color: white;
background-color: ${props => props.fancy ? 'red' : 'black'};
`
render() {
return (
<div>
<Button>Button</Button>
<Button fancy>Fancy</Button>
</div>
)
)
If you just want an a that has the exact styles as your Button then you can do <Button as=“a” />
You could also add some custom css like this:
const ButtonBase = styled.button`
// some css here
`
const SmallButtonCustom = `
// some css here
`
const SmallButton = styled(ButtonBase)`
${SmallButtonCustom};
`

Categories