Skip to content

zhangfisher/flexstyled

Repository files navigation

FlexStyled

FlexStyled is a simple runtime css-in-js library for react component

中文

  • zero dependencies.
  • runtime css generation.
  • support css variables.
  • support nested css.
  • support props dynamic css.
  • typescript support.

Open demo on CodeSandbox

Installation

pnpm add FlexStyled
# or
npm install FlexStyled
# or
yarn add FlexStyled

Usage

We plan to develop a Card component, which has a title attribute for displaying titles, a footer' attribute for displaying bottom content, and a children attribute as the content area of the card.

Basic Usage

import { styled } from "FlexStyled" 

export type  CardProps = React.PropsWithChildren<{
    title:string 
    footer?:string
  }>

export const Card = styled<CardProps>((props,{className})=>{
    const { title,children,footer} =props
    return (
      <div  className={className}>
        <div className="title">            
            {title}
        </div>
        <div className="content">{children}</div>
        <div className="footer">{footer}</div>
      </div>
    )
  },{  
    position:"relative",
    width:"100%",
    border:"1px solid #ccc",
    borderRadius:"4px" 
  })

-The above code will create a Card component, generate a style class (with a randomly generated name) for the style, and insert it into the head tag. -Then pass the className prop to the component, which will use this class name to apply the style.

You can find a CSS style similar to this in the head, where the className and style.id are both automatically generated. You can also specify id and className through the options parameter.

<style id="6rxqfu">
.sw6y3s4{
    position:relative;
    width:100%;
    border:1px solid #ccc;
    border-radius:4px;
}
</style>

Nested Style

Next, let's add styles to the title and footer of the Card component

export const Card = styled<CardProps>((props,{className})=>{
    const { title,children,footer} =props
    return (
      <div  className={className}>
        <div className="title">            
            {title}
        </div>
        <div className="content">{children}</div>
        <div className="footer">{footer}</div>
      </div>
    )},{  
      position:"relative",
      width:"100%",
      border:"1px solid #ccc",
      borderRadius:"4px",
      "& > .title":{
        fontSize:"20px",
        fontWeight:"bold",
      },
      "& > .footer":{
        borderTop:"1px solid #ccc",
        padding:"8px",
        textAlign:"right"
      }
  })

-We have added styles to the title and footer above. -Use the & symbol to represent the current parent element, similar to the syntax of nested CSS such as less and sass.

The style generated in head is as follows:

<style id="6rxqfu">
.sw6y3s4{
    position:relative;
    width:100%;
    border:1px solid #ccc;
    border-radius:4px;
}
.sw6y3s4 > .title{
    font-size:20px;
    font-weight:bold;
}
.sw6y3s4 > .footer{
    border-top:1px solid #ccc;
    padding:8px;
    text-align:right;
}
</style>

Dynamic Style

FlexStyled supports using props to dynamically set styles.

For example, we want the background color of the content card to be specified by the props.bgColor attribute.

export const Card = styled<CardProps>((props,{className,getStyle})=>{
    const { title,children,footer} =props
    return (
      <div  className={className} style={getStyle()}>
        <div className="title">            
            {title}
        </div>
        <div className="content">{children}</div>
        <div className="footer">{footer}</div>
      </div>
    )},{ 
      position:"relative",
      width:"100%",
      border:"1px solid #ccc",
      borderRadius:"4px",
      "& > .title":{
        fontSize:"20px",
        fontWeight:"bold",
      },
      "& > .footer":{
        borderTop:"1px solid #ccc",
        padding:"8px",
        textAlign:"right"
      },
      "& > .content":{
        padding:"8px",
        backgroundColor:(props)=>props.bgColor
      }
  })
  • The above code uses props.bgColor to dynamically set the background color of the content card.
  • In order to support dynamic properties, we need to use the getStyle function to get the dynamic style and inject it into the root element of the component.
  • The getStyle function returns a css style object that can be passed directly to the style attribute.
  • Any css property can use (props)=>{....} to dynamically generate CSS property values.

CSS Variables

FlexStyled supports using css variables.

We can use css variables in the root style declaration, and then use the setVar function to dynamically modify the css variable in the component.

export const Card = styled<CardProps>((props,{className,getStyle})=>{
    const { title,children,footer} =props
    const [primaryColor,setPrimaryColor] = React.useState("blue")
    return (
      <div className={className} style={getStyle({"--primary-color":primaryColor})}>
        </div>
        <div className="content">{children}</div>
        <div className="footer">{footer}</div>
      </div>
    )},{ 
      position:"relative",
      width:"100%",
      border:"1px solid #ccc",
      borderRadius:"4px",
      "--primary-color":"blue",
      "& > .title":{
        fontSize:"20px",
        fontWeight:"bold",
        color:"var(--primary-color)"
      },
      "& > .footer":{
        borderTop:"1px solid #ccc",
        padding:"8px",
        textAlign:"right"
      },
      "& > .content":{
        padding:"8px",
        backgroundColor:(props)=>props.bgColor
      }
  })
  • The above code uses css variables.
  • We declare a --primary-color css variable in the root style.
  • Then we use the --primary-color variable in the title style.
  • In order to modify the css variable, we need to introduce ref and pass ref to the root element, and then use the setVar function to modify the css variable.

Summary

FlexStyled is a very simple css-in-js library that can help you quickly encapsulate react components and support css variables and dynamic css properties.

  • By default, you only need to reference className in the component.
  • If you need to dynamically modify css variables, you need to introduce ref, pass ref to the root element, and then use the setVar function to modify css variables.
  • If you need to use props dynamic css properties, you need to use the getStyle function to get the dynamic css style and inject it into the root element.

creteStyle

styled function can also be used to create styles only and insert into head.

// card.style.ts

import { styled } from "FlexStyled"

// create style and insert into head
export default styled({ // 组件样式
      position:"relative",
      width:"100%",
      border:"1px solid #ccc",
      borderRadius:"4px",
      "--primary-color":"blue",
      "& > .title":{
        fontSize:"20px",
        fontWeight:"bold",
        color:"var(--primary-color)"
      },
      "& > .footer":{
        borderTop:"1px solid #ccc",
        padding:"8px",
        textAlign:"right"
      },
      "& > .content":{
        padding:"8px",
        backgroundColor:(props)=>props.bgColor
      }
})

// card.tsx
import cardStyle from "./card.style"

export default (props:CardProps)=>{
    return (
      <div className={cardStyle.className} style={cardStyle.getStyle({"--title-color":titleColor},props)}>
        <div className="title">            
            {props.title}
        </div>
        <div className="content">{props.children}</div>
        <div className="footer">{props.footer}</div>
      </div>
    )
  }

using createStyle.props to simplify the parameter passing, as follows:

 
export default (props:CardProps)=>{
    return (
      <div {...cardStyle.props()}>
          ...
      </div>
    )
  }

<div {...cardStyle.props({"--title-color":titleColor})}/>
<div {...cardStyle.props({"--title-color":titleColor},{props})}/>
<div {...cardStyle.props({"--title-color":titleColor},{props,className:"xxxxx xxxx"})}/>

Hook

FlexStyled also provides a hook useStyled to help you quickly encapsulate react components.

import { useStyle } from "FlexStyled"
export const Card2:React.FC<React.PropsWithChildren<CardProps>> = ((props:CardProps)=>{
    const { title } = props
    const [titleColor,setTitleColor] = useState("blue")
    const {className,getStyle } =  useStyle({
        // style 
    })
    return (
      <div className={className} style={getStyle({"--title-color":titleColor},props)}>
        <div className="title">            
            <span>{title}</span>
            <span className="tools"><button onClick={()=>setTitleColor(getRandColor())}>Change</button></span>
        </div>
        <div className="content">          
            {props.children}
        </div>
        <div className="footer">{props.footer}</div>
      </div>
    )
  })
  • The useStyle hook returns className and getStyle, which are used to inject style class names and dynamic styles.
  • The getStyle function returns a css style object that can be passed directly to the style attribute.
  • The useStyle hook supports passing options parameters to configure id and className.
  • The useStyle hook is the same as the styled function, the only difference is that the style sheet injected into the head will be automatically removed when the component is uninstalled.

Create Styled component

FlexStyled supports creating styled components, use html tag name.

import { styled } from "FlexStyled"

const MyButton = styled.div({
    color:"red",
    "&:hover":{
        color:"blue"
    }
})
  • You can also create other HTML tags, such as span, button, etc.

Performance

Due to the limitations of css-in-js, there may be performance issues. A recommended performance optimization method is to create all styles at once during the application's startup phase and insert them into the head, and then reference the styles in the component.

// styles.tsx
import { styled } from "FlexStyled"
export style1 = styled({...})
export style2 = styled({...})
export style3 = styled({...})

Options

// styled(<React.FC>,<styles>,<options>)

export interface StyledOptions{
    // The ID of the style sheet, if not specified, will be automatically generated
    id?:string                          
    // The generated class name, if not specified, will be automatically generated
    className?:string                        
}