0
votes

I'm trying to style this component with Styled Components, but my solution seems hackish. What is the best practice for styling this component with Styled Components?

Using Plain HTML and CSS, and I can create this:

HTML:

<label>
  <input type="checkbox" value="s1"/>
  <div class="container">s1</div>
</label>
<label>
  <input type="checkbox" value="s2"/>
  <div class="container">s2</div>
</label>
<label>
  <input type="checkbox" value="s3"/>
  <div class="container">s3</div>
</label>
<label>
  <input type="checkbox" value="s4"/>
  <div class="container">s4</div>
</label>

CSS:

input[type=checkbox] {
  position: absolute;
  width: 0;    
}

.container {
  width: 5em;
  border: solid #aaa 1px;
  background: #fff
  color: #000;
}
.container:hover {
  background: #999;
}  

.container:active {
  background: #333;
  color:#fff
}

input[type=checkbox]:checked + .container {   
  background: #000;
  color: #fff;
}    

input[type=checkbox]:checked + .container:hover {
  background: #ddd;
}  

input[type=checkbox]:checked + .container:hover:active {
  background: white;
  color: black;
}  

Using a React component and Styled Components, I can also create it, but I don't like using two different Styled Components and the ternary to accomplish what I could in CSS using input[type=checkbox]:checked + .container.

import React, { useState } from 'react';
import styled from 'styled-components'

function Test() {
  const [selectedStations, setSelectedStations] = useState([]);  

  const Input = styled.input`
    position: absolute;
    width: 0;    
  ` 
  const UncheckedContainer = styled.div` 
    width: 5em;    
    border: solid #aaa 1px;
    background: #fff;
    color: #000;

  &:hover {
    background: #999;
  }  

  &:active {
      background: #333;
      color: #fff;
  }
  `
  const CheckedContainer = styled.div`
    width: 5em;
    border: solid black 1px;
    background: #000;
    color: #fff;

  &:hover {
    background: #ddd;
  }  

  &:active {
      background: #fff;
      color: #000;
  }  
  `

  function selectStations(e) {    
    let station = e.target.value;
    const s = [...selectedStations]
    const stationIndex = s.indexOf(station)

    if (stationIndex > -1) {
      s.splice(stationIndex, 1);
    } else {
      s.push(station);
      s.sort();
    }

    setSelectedStations(s)
  };


  return (
    <div>
        {new Array(4).fill('').map((v, i) =>{

          let checked = selectedStations.indexOf(`s${i+1}`) > -1         

          return(
            <label key={`station${i + 1}`}>
            <Input              
              type="checkbox"
              value={`s${i+1}`}
              checked={checked}
              onChange={(e)=>selectStations(e)}              
            />

            {checked ? 
              <CheckedContainer>
                  {`s${i+1}`}
              </CheckedContainer>            
            : 
              <UncheckedContainer>
                  {`Station ${i+1}`}
              </UncheckedContainer>
            }


          </label>
          )}
        )}
    </div>
  )
}

export default Test;

Is is possible to clean this up a little but still use Styled Components?

2

2 Answers

0
votes

You can access props inside a styled-component as well as use sass like nesting to target children.

const Checkbox = styled.label`
  postion: relative;
  input[type="checkbox"] {
    // style here
  }
  .inner-checkbox {
    color: ${props => props.checked ? "red" : "blue"};
    // style here
  }
`

const List = props => {
  return props.list.map(item => (
    <Checkbox key={item.id} checked={item.checked}>
      <input checked={item.checked} type="checkbox" onChange={this.updateState} />
      <div className="inner-checkbox" />
    </Checkbox>
  ))
}

Another thing I would strongly avoid is creating styled components inside another component.

0
votes

You can have the state of the checkbox in a variable and pass it to the styled component like:

<StyledComponent isChecked={isChecked} />

Then in the styled component:

const StyledComponent = styled.input`

 color: ${props => props.isChecked ? '#fff' : '#000'};

`

.. and so on for background-color and border.