I have made a button component using styled-components like below.
And I am using this button component in another component, even made a parent component to the button component, hoping that if the parent component has "text-align: center" attribute, it'd center texts in my button, which turned out not... maybe I did not do it right?
Below is my code for Button.js
import React from "react";
import styled, { css } from "styled-components";
const Container = styled.div`
text-align: center;
`;
const colorStyles = css`
${(props) =>
props.color === "blue" &&
css`
background: #588ced;
color: #ffffff;
&:hover {
background: #3866bc;
}
`}
${(props) =>
props.color === "gray" &&
css`
background: #eeeef2;
color: #65636a;
&:hover {
background: #dbdadf;
}
`}
`;
const sizeStyles = css`
${(props) =>
props.size === "normal" &&
css`
width: 181px;
height: 48px;
`}
${(props) =>
props.size === "small" &&
css`
width: 100px;
height: 40px;
`}
`;
const shapeStyles = css`
${(props) =>
props.shape === "round" &&
css`
border-radius: 50px;
`}
${(props) =>
props.shape === "squared" &&
css`
border-radius: 10px;
`}
`;
const StyledButton = styled.button`
/* common styles */
display: flex;
outline: none;
border: none;
font-family: Roboto;
font-style: normal;
font-weight: bold;
font-size: 16px;
line-height: 19px;
cursor: pointer;
align-items: center;
text-align: center;
/* sizes */
${sizeStyles}
/* colors */
${colorStyles}
/* shapes */
${shapeStyles}
/* etc */
// & + & {
// margin-left: 1rem;
// }
`;
function Button({ children, color, size, shape, ...rest }) {
return (
<Container>
<div>
<StyledButton color={color} size={size} shape={shape} {...rest}>
{children}
</StyledButton>
</div>
</Container>
);
}
export default Button;
AND this is a part from the other component where the button component is used.
import styles from "./Page.module.css";
import { FontAwesomeIcon } from "#fortawesome/react-fontawesome";
import Button from "../button/Button";
export default function Page({ data, setModalVisibleState }) {
const addButtonHandler = () => {
setModalVisibleState(true);
};
return (
<div className={styles.container}>
<div className={styles.titleBox}>
<FontAwesomeIcon icon={faGraduationCap} />
<p>{data.title}</p>
<div className={styles.buttonWrap}>
<Button size="normal" shape="squared" color="blue" onClick={addButtonHandler}>{data.button}</Button>
</div>
</div>
//...and more...
And under "./Page.module.css" I have done this.
.buttonWrap {
display: flex;
text-align: center;
align-items: center;
}
This is how my buttons look right now. Before I used text-align and align-items attributes the text was located on the upper left side of the button. Is there an way to center texts in the button?
I am wondering if the purple box on the right has anything to do with centering the text..
Thank you very much in advance for reading and kindly answering my question. :)
Just add: justify-content: center; Aligning Items in a Flex Container
To center our box we use the align-items property to align our item on the cross axis, which in this case is the block axis running vertically. We use justify-content to align the item on the main axis, which in this case the inline axis running horizontally.
DEMO
button{
display: flex;
outline: none;
border: none;
font-family: Roboto;
font-style: normal;
font-weight: bold;
font-size: 16px;
line-height: 19px;
cursor: pointer;
align-items: center;
text-align: center;
border-radius: 50px;
background: #588ced;
color: #ffffff;
width:189px;
height:48px;
}
<button>button</button>
<button style="justify-content: center;">button</button>
Related
I created this react app with a side bar, and it looks pretty good, but one thing I can't figure out is how to mark the item (page link) that was clicked.
Here is the code, I added activeClassName="selected" to the SidebarLink const, and added it to the styled-component, but it's not working.
import React, { useState } from "react";
import { Link } from "react-router-dom";
import styled from "styled-components";
const SidebarLink = styled(Link)`
/*border: 3px solid green;*/
display: flex;
color: #e1e9fc;
justify-content: space-between;
align-items: center;
padding: 20px;
list-style: none;
height: 60px;
text-decoration: none;
font-size: 18px;
&:hover {
background: #252831;
border-left: 4px solid #ffbc2e;
cursor: pointer;
}
.selected {
color: red;
}
`;
const SidebarLabel = styled.span`
margin-left: 16px;
`;
/* Used for Training dropdown */
const DropdownLink = styled(Link)`
/*border: 3px solid red;*/
background: #252831;
height: 60px;
padding-left: 3rem;
display: flex;
align-items: center;
text-decoration: none;
color: #f5f5f5;
font-size: 16px;
font-weight: bold;
&:hover {
background: #48526F;
border-left: 4px solid #ffbc2e;
cursor: pointer;
}
`;
const SubMenu = ({ item }) => {
const [subnav, setSubnav] = useState(false);
const showSubnav = () => setSubnav(!subnav);
return (
<>
<SidebarLink to={item.path} onClick={item.subNav && showSubnav} activeClassName="selected" exact>
<div className="sidebarTextContainer">
<div className="sidebarIcon">{item.icon}</div>
<div className="sidebarText"><SidebarLabel>{item.title}</SidebarLabel></div>
</div>
<div>
{item.subNav && subnav
? item.iconOpened
: item.subNav
? item.iconClosed
: null}
</div>
</SidebarLink>
{subnav &&
item.subNav.map((item, index) => {
return (
<DropdownLink to={item.path} key={index}>
{item.icon}
<SidebarLabel>{item.title}</SidebarLabel>
</DropdownLink>
);
})}
</>
);
};
export default SubMenu;
Anyone got any idea please on how to fix this?
Even though you're asking about remembering which item was clicked, I think what you actually want is that the link corresponding to the current page be highlighted as active.
If you're using react-router v6, you should use NavLink instead of Link (docs).
A <NavLink> is a special kind of <Link> that knows whether or not it is "active".
By default, an active class is added to a <NavLink> component when it is active.
You should update the imported component and remove the activeClassName.
const SidebarLink = styled(NavLink)`
// ...
&.active {
color: red;
}
`
// ...
<SidebarLink to={item.path} onClick={item.subNav && showSubnav} exact>
...
</SidebarLink>
If you're using react-router v5, then you should also use NavLink but you have to pass the activeClassName, like you do in your example. In this case you simply need to substitute Link for NavLink.
Also note that you nested CSS syntax is incorrect. It is applied to the children of SidebarLink, not on SidebarLink itself. You'd want to replace .selected { color: red; } with &.selected { color: red }.
I have a main page, with a button component i create to reuse in my project. My issue is when i add the onClick event to my external component, the click event is not working, but is i create the same button inside my main page, the click event works just fine
Button Component
import React from "react";
import styled from "styled-components";
const BigButton = (props): JSX.Element => {
return <>{props.red ? <BigBtn red={props.red}>{props.val}</BigBtn> : <BigBtn>{props.val}</BigBtn>}</>;
};
export default BigButton;
const BigBtn = styled.button`
font-style: normal;
font-weight: 500;
font-size: 12px;
line-height: 15px;
color: #f5f5f5;
width: 78px;
height: 30px;
background: ${(props) => (props.red ? "#BD2129" : "#2e3034")};
border: ${(props) => (props.red ? "initial" : "1px solid #494b4f")};
border-radius: 2px;
display: flex;
align-items: center;
justify-content: center;
`;
This works on main page
<button onClick={buttonClose}>Close</button>
Button Component on main page - This doesn't work on main page
<BigButton val="Cancel" onClick={handleClose} />
Close function
const handleClose = (e) => {
e.preventDefault();
props.onClose();
};
Your component does not look correct. You do not have button inside the component and onClick event. You should update like this
import React from "react";
import styled from "styled-components";
const BigButton = (props): JSX.Element => {
const handleClick = () => {
props.onClick()
}
return <>{props.red ?
<BigBtn red={props.red}>
<button onClick={handleClick}>
{props.val}
</button>
</BigBtn> :
<BigBtn>
<button onClick={handleClick}>
{props.val}
</button>
</BigBtn>}
</>
};
export default BigButton;
const BigBtn = styled.button`
font-style: normal;
font-weight: 500;
font-size: 12px;
line-height: 15px;
color: #f5f5f5;
width: 78px;
height: 30px;
background: ${(props) => (props.red ? "#BD2129" : "#2e3034")};
border: ${(props) => (props.red ? "initial" : "1px solid #494b4f")};
border-radius: 2px;
display: flex;
align-items: center;
justify-content: center;
`;
Error:
./src/card.js
Attempted import error: 'Bottom' is not exported from './styles/cards.style'.
card.js
import React from 'react'
import {
Bottom,
Color,
Text,
Image
} from "./styles/cards.style";
function Card(props) {
return (
<div>
<Bottom>
<Color />
<Text>{props.text}</Text>
<Text>{props.text}</Text>
</Bottom>
<Image
alt=""
src={props.image}
/>
</div>
);
}
export default Card;
cards.style
import styled from "styled-components";
export default {
colors: {
black: "rgba(0,0,0,1)",
brandPrimary: "rgba(238,120,36,1)",
brandPrimaryLight: "rgba(255,184,8,1)",
brandTertiary: "rgba(0,65,125,1)",
darkSlateGray: "rgba(51,51,51,1)",
white: "rgba(255,255,255,1)"
},
fonts: {
uiMainContent: {
family: "Poppins",
size: "15px",
weight: "300",
lineHeight: "21px"
},
uiSubContent: {
family: "Poppins",
size: "13px",
weight: "300",
lineHeight: "20px"
}
}
};
export const Bottom = styled.div`
width: 100%;
height: calc(100% - 20px);
background-color: ${props => props.theme.colors.white};
border-radius: 4px;
padding: 0 0 20px;
display: flex;
flex-direction: column;
align-items: flex-start;
position: relative;
`;
export const Color = styled.div`
height: 120px;
background-color: ${props =>
props.theme.colors.brandPrimary};
margin-bottom: 16px;
border-radius: 4px 4px 0px 0px;
align-self: stretch;
`;
export const Text = styled.p`
color: ${props => props.theme.colors.black};
margin-left: 16px;
letter-spacing: 0.1px;
font-family: ${props =>
props.theme.fonts.uiSubContent.family};
font-size: ${props =>
props.theme.fonts.uiSubContent.size};
font-weight: ${props =>
props.theme.fonts.uiSubContent.weight};
line-height: ${props =>
props.theme.fonts.uiSubContent.lineHeight};
&:not(:last-of-type) {
margin-bottom: 4px;
}
`;
export const Image = styled.img`
width: 150px;
height: 92px;
position: absolute;
left: 29px;
top: 14px;
`;
I am trying to build cards in reactjs. I usually stick to scss however cannot use props with scss which I will have to use later to dynamically generate components. Not sure what is wrong here as I did export Button. Please can someone shed some insight you see what is so blatantly wrong it is causing this error.
in that case you have an export default as the first thing of you code, if you are exporting more than one thing from the same file, you should stick to exporting each const/function by itself and not having any export default
If you are using ReactJS/NextJS I would really recommend creating a global theme that you normally write and import with the application, so you could have things like
// global.js
const GlobalStyle = createGlobalStyle`
* {
box-sizing: border-box;
}
:root {
--black: rgba(0,0,0,1);
--brandPrimary: rgba(238,120,36,1);
...
...
`
}
Take a look here, it should help you a lot.
I have these styled components:
import styled, { css } from 'styled-components'
const Container = styled.div`
background-color: ${props => props.theme === "light" ? "hsl(0, 0%, 98%)" : "hsl(235, 24%, 19%)" };
border-radius: 4px;
box-shadow: ${props => props.theme === "light" ? "0px 35px 50px -15px rgba(194, 195, 214, 0.5)" : "0px 35px 50px -15px rgba(0, 0, 0, 0.5)" };
`
const Footer = styled.footer`
padding: 25px;
display: flex;
align-items: center;
justify-content: space-between;
color: ${props => props.theme === "light" ? "hsl(236, 9%, 61%)" : "hsl(234, 11%, 52%)" };
font-size: 14px;
`
const Nav = styled.nav`
display: flex;
align-items: center;
justify-content: space-between;
color: inherit;
`
const NavItem = styled.a`
cursor: pointer;
display: inline-block;
color: inherit;
&:hover {
color: ${props => props.theme === "light" ? "hsl(235, 19%, 35%)" : "hsl(236, 33%, 92%)" };
}
&:not(:last-of-type) {
margin-right: 15px;
}
${({ active }) =>
active &&
css`
color: hsl(220, 98%, 61%);
`
}
`
const ClearList = styled.p`
color: inherit;
cursor: pointer;
&:hover {
color: ${props => props.theme === "light" ? "hsl(235, 19%, 35%)" : "hsl(234, 39%, 85%)" };
}
`
And I have this component called TodoList with a theme props that get passed to the styled components like this:
const TodoList = ({ theme }) => {
return (
<Container theme={theme}>
<Footer theme={theme}>
<p>5 items left</p>
<Nav>
<NavItem theme={theme}>All</NavItem>
<NavItem theme={theme}>Active</NavItem>
<NavItem theme={theme}>Completed</NavItem>
</Nav>
<ClearList theme={theme}>Clear Completed</ClearList>
</Footer>
</Container>
)
}
export default TodoList
How do I call the theme props only once in the Container element and make it accessible to all the child elements such as Clearlist, NavItems and Footer without explicitly passing them
Thank you
You can solve this issue by using ThemeProvider
visit this link https://styled-components.com/docs/advanced
Example
// Define our button, but with the use of props.theme this time
const Button = styled.button`
color: ${props => props.theme.fg};
border: 2px solid ${props => props.theme.fg};
background: ${props => props.theme.bg};
font-size: 1em;
margin: 1em;
padding: 0.25em 1em;
border-radius: 3px;
`;
// Define our `fg` and `bg` on the theme
const theme = {
fg: "palevioletred",
bg: "white"
};
// This theme swaps `fg` and `bg`
const invertTheme = ({ fg, bg }) => ({
fg: bg,
bg: fg
});
render(
<ThemeProvider theme={theme}>
<div>
<Button>Default Theme</Button>
<ThemeProvider theme={invertTheme}>
<Button>Inverted Theme</Button>
</ThemeProvider>
</div>
</ThemeProvider>
);
I have this code:
import React, {useState} from 'react';
import PropTypes from 'prop-types';
import styled from "styled-components";
import ArrowTemplate from "./ArrowTemplate";
const AccordionBtn = styled.button`
background-color: #eee;
color: #444;
cursor: pointer;
padding: 18px;
display: flex;
align-items: center;
border: none;
outline: none;
transition: background-color 0.6s ease;
:hover,
:focus,
:active {
background-color: #ccc;
}
`;
const AccordionTitle = styled.p`
font-family: "Open Sans", sans-serif;
font-weight: 600;
font-size: 14px;
`;
const AccordionContent = styled.div`
background-color: red;
overflow: hidden;
transition: 0.6s;
`;
const AccordionText = styled.div`
font-family: "Open Sans", sans-serif;
font-weight: 400;
font-size: 14px;
padding: 18px;
`;
const AccordionSection = styled.div`
display: flex;
flex-direction: column;
`;
Accordion.propTypes = {
title: PropTypes.string.isRequired,
content: PropTypes.node.isRequired,
id: PropTypes.string.isRequired,
};
function Accordion(props) {
const [isAccordionExpanded, setIsAccordionExpanded] = useState(false);
const toggleAccordion = () => {
setIsAccordionExpanded(!isAccordionExpanded);
};
return (
<AccordionSection>
<AccordionBtn onClick={toggleAccordion}>
<AccordionTitle>
{props.title}
</AccordionTitle>
<ArrowTemplate
color={'black'}
direction={isAccordionExpanded === true ? 'up' : 'down'}
onClick={toggleAccordion}
/>
</AccordionBtn>
<AccordionContent
style={{height: isAccordionExpanded === true ? "100px" : "0"}}
>
<AccordionText>
{props.content}
</AccordionText>
</AccordionContent>
</AccordionSection>
);
}
export default Accordion;
What this code does, is extends the accordeon on click. Preety simple. But now, I want to move this height:
<AccordionContent
style={{height: isAccordionExpanded === true ? "100px" : "0"}}
>
here:
const AccordionContent = styled.div`
background-color: red;
overflow: hidden;
transition: 0.6s;
`;
The problem is I need to use state, and if I declare it in the function, it will get re-rendered, and not run the animation. How can I pass the state to the styled-component?
You can just pass the isAccordionExpanded directly to the styled component. Change your style from this:
const AccordionContent = styled.div`
background-color: red;
overflow: hidden;
transition: 0.6s;
`;
to include this
height: ${({ isAccordionExpanded }) => (isAccordionExpanded ? "100px" : "0"};
Another way to do animation in React is using React Transition Group, Quite good