I have problem with understanding how cascade style sheet algorithm works with React library styled-component. I have created Todo element with 3 fields, and I gave each its own class , like TodoTitle, TodoDescription and TodoAction - so that div with different class will have diferent width. Yet width property does not apply to class, even though it is flex. Other properties like red backgound did aplied.
import React from 'react';
import { Todo } from '../../types/Todo';
import { StyledTodoItem } from './TodoItem.styles';
type Props = {
todo: Todo;
index: number;
};
export const TodoItem: React.FC<Props> = ({ todo, index }) => {
return (
<StyledTodoItem bg={index % 2 === 0 ? 'even' : 'odd'}>
<div className="todoTitle">
<h1>{todo.title}</h1>
</div>
<div className="todoDescription">
<h1>{todo.text}</h1>
</div>
<div className="todoAction">
<button type="button">Hello</button>
</div>
</StyledTodoItem>
);
};
import styled from 'styled-components';
type Props = {
bg: 'even' | 'odd';
};
export const StyledTodoItem = styled.div<Props>`
width: 100%;
display: flex;
background: ${({ bg }) => (bg === 'odd' ? 'white' : '#ededed')};
height: ${({ theme }) => theme.todoTable.elementMinHeight};
border-right: 1px solid ${({ theme }) => theme.palette.borderColor};
.todoTitle {
display: flex;
justify-content: center;
padding: 10px 20px;
width: 150px;
background: red;
}
`;
If , instead of .todoTitle I paste div , width will be applied, but for every element.
Please, explain how to fix this and any similar problem like this that might arise.
Thanks in advance.
Thanks for comments. So I checked out few tutorials and it seems that best practice is to avaid as possible html tags and to change them for custom styled components as provided in example. Also nested components do lead to lots of bugs, or so it seems.
return (
<>
<TodoItemWrapper bg={index % 2 === 0 ? 'even' : 'odd'}>
<TodoTitle>
<TextTitle>{todo.title}</TextTitle>
</TodoTitle>
<TodoDescr>
<TextDescr>{todo.text}</TextDescr>
</TodoDescr>
<TodoAction>
<Button>View</Button>
<Button>Edit</Button>
<Button>Delete</Button>
</TodoAction>
</TodoItemWrapper>
)
export const TodoItemWrapper = styled.div`
width: 100%;
display: flex;
background: ${({ bg }) => (bg === 'odd' ? 'white' : '#ecfcff')};
height: fit-content;
`;
export const TodoText = styled.div`
display: flex;
justify-content: flex-start;
align-items: center;
padding: 0 20px;
`;
export const TodoTitle = styled(TodoText)`
width: ${({ theme }) => theme.todoTable.title};
border-right: 1px solid ${({ theme }) => theme.palette.borderColor};
`;
Related
I am trying to create a Styled Component which can receive a prop for the type of border. The Section can either have border-top or border-bottom based on the prop passed to it.
Here is the code
type TSectionBorder = 'top' | 'bottom';
<Section edge="top">
{children}
</Section>
How can I achieve this in styled components, something along these lines -
const Section = styled.section<{edge: TSectionBorder}>`
border-${edge}: 1px solid black;
`;
const Section = styled.section<{edge: TSectionBorder}>`
${({ edge }) => `border-${edge}: 1px solid black`};
`;
I deconstructed the props just to keep your syntax, however, this is one way to do so. They also have a css helper to look into.
Documentation:
https://styled-components.com/docs/basics#passed-props
https://styled-components.com/docs/api#css
First you will import:
import styled from 'styled-components';
Here is the function ButtonBlock:
function ButtonBlock(props) {
return (
<BtnBlock type="primary" {...props}>
{props.children}
</BtnBlock>
);
}
const handleColorType = (color) => {
switch (color) {
case 'grey':
return '#D4D5D6';
default:
return '#6633CC';
}
};
const hoverColorType = (color) => {
switch (color) {
case 'grey':
return '#6a7581';
default:
return '#99CC00';
}
};
Styled Component:
const BtnBlock = styled(Button)`
&& {
max-width: none;
width: 100%;
text-align: center;
display: block;
font-size: 14px;
font-weight: 600;
line-height: 50px;
height: 50px;
text-transform: uppercase;
border-radius: 0;
border: 0px;
padding: 0;
background: ${({ color }) => handleColorType(color)};
&[disabled] {
background-color: #c6c6c6;
}
:hover {
background-color: ${({ color }) => hoverColorType(color)};
}
&[disabled]:hover {
background-color: #c6c6c6;
}
}
`;
this is my styled component
import React from 'react';
import styled, {css} from 'styled-components';
const CategoryButton = styled(Button).attrs({
variant: 'outlined',
})`
${({ theme }) => css`
display: flex;
align-items: center;
justify-content: center;
padding: ${theme.spacing(2)}px ${theme.spacing(4)}px;
background-color: ${(props) =>
props.selected ? theme.colors.primary.main : theme.colors.background};
color: ${(props) =>
props.selected ? theme.colors.background : theme.colors.primary.main};
border-color: ${(props) =>
props.selected ? theme.colors.primary.main : '#c8c8c8'};
`}
`;
it returns an error that said
Property 'selected' does not exist on type 'ThemeProps<DefaultTheme>'
I've tried to implement the suggestion on this other question to specify the props to pass to component
interface CategoryButton {
selected?: boolean;
}
const CategoryButton = styled(Button).attrs({
variant: 'outlined',
})<CategoryButton>`
${({ theme }) => css`
...
`}
`;
Nevertheless, the same error still appears. How can I get this to work?
You have to pass in selected in CategoryButton like this. It works same as props passing to child:
<CategoryButton selected={true}/>
I'm trying to style my component which was styled already. But styles in the new rules are not applied in output CSS.
Can I style component that I already styled?
Thanks you in advance for your help.
EDIT: Add rest of LanugageChooser definition
// COMPONENT THAT I'M TRYING TO STYLE
const LanguageChooser = () => {
const Container = styled.div`
display: flex;
align-items: center;
height: 36px;
& > div:not(:last-child) {
margin-right: 5px;
}
`;
return (
<Container>
<Flag { ...languages.pl }/>
<Flag { ...languages.en }/>
</Container>
)
}
const Flag = ({ flag, language }) => {
const { i18n } = useTranslation();
const Button = styled.div`
cursor: pointer;
font-size: 24px;
transition: .2s all;
&:hover {
font-size: 36px;
}
`;
return (
<Button onClick={ () => i18n.changeLanguage(language) }>{ flag }</Button>
)
}
// TRYING ADD MARGIN LEFT, BUT THERE IS NO RESULT.
// ANY OTHER CSS PROPERTY ARE NOT APPLYING
const Navbar = ({ color }) => {
...
const StyledLanguageChooser = styled(LanguageChooser)`
margin-left: auto;
`;
const Nav = styled.nav`
display: flex;
align-content:center;
background: ${ color };
padding: 2px 3px;
`;
return (
<Nav className="navbar">
<StyledNavLink to="/home">...</StyledNavLink>
<StyledNavLink to="/maps">...</StyledNavLink>
<StyledNavLink to="/charts">...</StyledNavLink>
<StyledLanguageChooser/>
</Nav>
)
}
First, move the styled component outside of function scope or your style will reset on every render and you will get heavy performance issues.
Secondly, in order to apply styles, you need to pass className property.
See Styling normal React components
If you use the styled(MyComponent) notation and MyComponent does not render the passed-in className prop, then no styles will be applied.
const Container = styled.div`
display: flex;
align-items: center;
height: 36px;
& > div:not(:last-child) {
margin-right: 5px;
}
`;
const LanguageChooser = ({ className }) => {
return (
<Container className={className}>
<Flag {...languages.pl} />
<Flag {...languages.en} />
</Container>
);
};
const Button = styled.div`
cursor: pointer;
font-size: 24px;
transition: 0.2s all;
&:hover {
font-size: 36px;
}
`;
const Flag = ({ flag, language }) => {
const { i18n } = useTranslation();
return <Button onClick={() => i18n.changeLanguage(language)}>{flag}</Button>;
};
I am creating Form Buttons where some would be Primary and some would be Secondary. On them, I would apply the UX color codes. The first one works, the second one doesn't.
Am I doing something wrong?
Here is the code:
const FormActionBtns = styled.span`
height: 50px;
max-width: 100px;
cursor: pointer;
display: flex;
justify-content: center;
align-items: center;
background-color: red;
color: white;
${props => props.theme && props.theme.ACCENT && `
font-size: ${props.theme.FONT_SIZE.TOPIC_HEADER};
color: ${props.theme.PRIMARY_STAGE.FORM_COMPONENTS.FONT.REVERSE_BRIGHT};
${props.isPrimary && `
background-color: ${props.theme.ACCENT.IDLE};
`}
${props.isSecondary && `
background-color: ${props.theme.COMPLEMENTARY.IDLE};
`}
`}
`;
export const SubmitBtn = (props) => {
console.log(props);
return(
<FormActionBtns isPrimary={true}>
{props.submitTxt}
</FormActionBtns>
)
}
export const RevertBtn = (props) => {
return (
<FormActionBtns isSecondary={true}>
{props.revertTxt}
</FormActionBtns>
)
}
The Submit button works brilliantly as the correct accent color gets applied based on the theme. The Reset button is not working properly having passed isSecondary=true.
Note: When I remove the condition isPrimary from the style, Reset then gives proper result
In order to implement CSS dynamically in React, I'd just use the style attribute, similarly to this code:
const FormActionBtns = (props) => {
const styleProp = { height: '50px', maxWidth: '100px' }; // etc.
if (props.isPrimary) {
styleProp.backgroundColor = props.theme.ACCENT.IDLE;
} else if (props.isSecondary) {
styleProp.backgroundColor = props.theme.COMPLEMENTARY.IDLE;
}
return (
<span style={styleProp}>
{props.children}
</span>
);
}
You can also use .attrs.
styled.span.attrs(({ isPrimary, theme }) => ({
background-color: isPrimary ? theme.ACCENT.IDLE : theme.COMPLEMENTARY.IDLE;
}))
`
// other styles
`
You're missing a semi-colon:
${props.isPrimary && `
background-color: ${props.theme.ACCENT.IDLE};
`}; // here
${props.isSecondary && `
background-color: ${props.theme.COMPLEMENTARY.IDLE};
`}
Trying to create a nested component that accepts multiple arguments, eg. styles for the button, styles for text and a possible icon. Everything (passing props) works fine if I directly render the button.
Below is the code I've written
import React from "react";
import styled from "styled-components";
import _ from "lodash";
import { hexToRgb } from "../styles/helpers";
import * as MaterialIcons from "styled-icons/material";
const StyledButton = styled.button`
text-align: center;
border: ${props =>
props.outline ? `1px solid ${props.outlineColor}` : "none"};
background: ${props => (props.background ? props.background : "#000")};
border-color: ${props =>
props.outlineColor ? props.outlineColor : "transparent"};
min-width: 120px;
width: ${props => (props.width ? props.width : "auto")};
min-height: 40px;
border-radius: 25px;
color: ${props => (props.color ? props.color : "#FFF")};
transition: all ease 0.5s;
&:hover {
cursor: pointer;
background: ${props =>
props.background ? hexToRgb(props.background) : "#FFF"};
}
`;
const StyledText = styled.span`
font-size: 16px;
font-weight: ${props => (props.fontWeight ? props.fontWeight : 400)};
`;
const StyledIcon = styled(MaterialIcons.Lock)`
font-size: 15;
padding: 10px;
`;
const Button = props => {
let _icon = null;
const { children, icon } = props;
console.log("props", props);
if (icon) {
_icon = <StyledIcon size="48" title="Test icon" />;
}
console.log("StyledButton", StyledButton);
return (
<StyledButton>
<StyledText>{children}</StyledText>
{_icon}
</StyledButton>
);
};
export default Button;
If I directly export default StyledButton it works fine.
For some odd reason props were not passed onto the StyledComponent, however directly stating what I want works.
return (
<StyledButton
outlineColor={outlineColor}
background={background}
width={width}
color={color}
outline={outline}
>
<StyledText>{children}</StyledText>
{_icon}
</StyledButton>
);