Styling various versions of a <button> using Styled Components

In my app, I have a basic colored button that needs to change its color depending on the UI state:

const StyledButton = styled.button`
  & {
    border: 0;
    color: white;
    cursor: pointer;
  }

  &:hover {
    background-color: ${(props) => props.theme.hoverColor};
  }
  &.base {
    background-color: ${(props) => props.theme.baseColor};
  }
  &.selected {
    background-color: ${(props) => props.theme.selectedColor};
  }
  &&.danger:hover {
    background-color: ${(props) => props.theme.dangerColor};
  }
  &.disabled {
    background-color: ${(props) => props.theme.disabledColor};
  }
`;

I used CSS Modules before to prevent classes from affecting other styles. The transition to styled components has been almost one-to-one.

.Button {
  border: 0;
  color: white;
  cursor: pointer;
}

.Button:hover {
  background-color: var(--hover-color);
}
.Button.base {
  background-color: var(--base-color);
}
.Button.selected {
  background-color: var(--selected-color);
}
.Button.Button.danger:hover {
  background-color: var(--danger-color);
}
.Button.disabled {
  background-color: var(--disabled-color);
}

While I appreciate the theming capabilities of Styled Components, I am not a fan of how my "local" classes are no longer contained within the component. It feels like a significant tradeoff to me.

Is there a better way to implement multiple variations of a component in this scenario?

Answer №1

Currently, I have come up with two solutions:

The first option is to manually calculate the final result without using any classes.


const StyledButton = styled.button`
  & {
    border: 0;
    color: white;
    cursor: pointer;
  }

  ${(props) =>
    props.isBase &&
    css`
      &,
      &:hover {
        background-color: ${(props) => props.theme.baseColor};
      }
    `}
  ${(props) =>
    props.selectedColor &&
    css`
      &,
      &:hover {
        background-color: ${(props) => props.theme.selectedColor};
      }
    `} 
  ${(props) =>
    props.isDisabled &&
    css`
      &,
      &:hover {
        background-color: ${(props) => props.theme.disabledColor};
      }
    `}
  ${(props) =>
    props.isDanger &&
    css`
      & {
        background-color: ${(props) => props.theme.dangerColor};
      }
      &:hover {
        background-color: ${(props) => props.theme.dangerColor};
      }
    `}
`;

Although it works, it does appear somewhat messy.

The second solution involves breaking down the StyledButton into multiple components:


const StyledButton = styled.button`
  & {
    border: 0;
    color: white;
    cursor: pointer;
    background-color: ${(props) => props.theme.baseColor};
  }
`;
const StyledSelectedButton = styled(StyledButton)`
  & {
    background-color: ${(props) => props.theme.selectedColor};
  }
`;

const StyledDisabledButton = styled(StyledButton)`
  & {
    background-color: ${(props) => props.theme.disabledColor};
  }
`;

const StyledDangerButton = styled(StyledButton)`
  & {
    background-color: ${(props) => props.theme.baseColorColor};
  }
  &&:hover {
    background-color: ${(props) => props.theme.dangerColor};
  }
`;

This approach may be superior, but there is a concern about potential complexity escalation when dealing with more intricate states (such as a selected danger component) or additional pseudo-classes.

Furthermore, if the button in question had a state, such as a click counter, modifying the style could reset the state.

I am struggling to devise a workaround for this issue.

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

I'm having trouble keeping my navbar fixed

Having trouble keeping my navigation bar fixed I attempted using the <nav class="fixed-nav-bar> with no success. I also tried adjusting it in CSS, but it still wouldn't stay fixed. <nav role="navigation" class="navbar navbar-default"> ...

What are some ways to display unprocessed image data on a website using JavaScript?

I have an API endpoint that provides image files in raw data format. How can I display this image data on a website using the img tag or CSS background-image property, without utilizing canvas? One possible approach is shown below: $.get({ url: '/ ...

What might be causing this error message to appear: "Invariant Violation: Unable to define both value and children"?

Why is this code showing an error (Invariant Violation: Cannot specify both value and children)? Every time I try to run this code, it displays the error Invariant Violation: Cannot specify both value and children. Can someone please help me figure out th ...

Cannot access pseudo elements in the Microsoft Edge browser

Is there a workaround for not being able to select or access the properties of :before & :after elements on Microsoft Edge's inspect element? .arrow_box { position: relative; background: #d43959; border: 4px solid #ac1f3c; width ...

Tips on customizing the appearance of a Fluent UI checkbox

I am having trouble styling the Fluent UI checkbox and changing the checkmark color when it is in an indeterminate state. I am using Flueint UI 8.x. "dependencies": { "react": "16.8.6", "@fluentui/react": & ...

Converting a DataFrame to a JSON series using Python

I have a Python Dataframe that looks like this: https://i.sstatic.net/rJMmJ.png My goal is to convert it into the JSON object format below, so I can send it to the frontend for plotting on highcharts: name: 'netflix', data: [ [1579996800 ...

Having trouble with the href attribute not properly redirecting to a different page

I am trying to create a hyperlink for an option that will take users to another page. All the other options on the page lead to different sections within the same page. When I add CONTACT, it doesn't work. All the files are in the same ...

What is the best way to adjust the mobile screen size to display the most optimized version of a web app's

Currently, I am utilizing react, Bootstrap, and some custom sass to construct and style the front end of my web application. While the design appears good on mobile devices, there is an issue where users need to pinch the screen due to it looking slightly ...

ReactJS - troubleshooting webcam video stream issue

I can't figure out why this code is not working properly. I am able to access the camera stream and see the light on my camera indicating that it's working, but the stream doesn't seem to be attaching correctly. class VideoOutput extends ...

What advantages does withStyles offer that surpasses makeStyles?

Is there a distinct purpose for each? At what point is it more suitable to utilize withStyles instead of makeStyles? ...

How does the use of nodejs, a server-side scripting language, tie into ReactJs or other front-end languages?

Can Node, being a server-side scripting language, be effectively utilized in the development of front-end applications such as npx create-react-app or npx create-nuxt-app? ...

Safari: The onbeforeunload event

Welcome! To experience the Leave | Stay confirmation box on your page, please make sure you are using Safari 10. I am currently facing an issue with getting the confirmation box to display properly across different browsers. To begin, please visit and l ...

How to display text on a new line using HTML and CSS?

How can I properly display formatted text in HTML with a text string from my database? The text should be contained within a <div class="col-xs-12"> and nested inside the div, there should be a <pre> tag. When the text size exceeds the width o ...

scrolling malfunction

I encountered a problem with the scroll feature in this image: The problem is that I only want the vertical scroll to show up, not the horizontal scroll. I tried using the overflow:scroll attribute in my code. Will it display the same in Firefox and IE, o ...

Issue where CSS modal does not appear when clicked after being toggled

I've been working on a custom modal design that I want to expand to fill the entire width and height of the screen, similar to how a Bootstrap modal functions. The goal is to have a container act as a background with another inner div for content How ...

Ways to align DataGrid columns to begin from the right in Material UI

In my project, I am using a data-grid of Material UI and I am looking to adjust the style so that all columns align from right to left. Each column should start on the right side. You can view the code on this sandbox. ...

Python Ordering menu items for the Pelican restaurant

In response to jcollado's advice on this thread, I have set up my menu as per his instructions. However, I am facing difficulty adding the active CSS to the active item. DISPLAY_PAGES_ON_MENU = False DISPLAY_CATEGORIES_ON_MENU = False MENUITEMS = ( ( ...

Is there a way to dynamically modify a website's default viewport settings in a mobile browser?

When viewing a website in Landscape mode, everything looks good. However, switching to Portrait mode displays the message "Screen size not supported." I decided to test this on my desktop browser and discovered that adjusting the initial-scale:1 to initial ...

Traditional method for comparing prevProps in componentDidUpdate

As I work on prototyping, I've noticed that when new props come in as an array of complex objects, prevProps.myProp===this.props.myProp always returns false. While using JSON.stringify for comparison seems to work, it doesn't feel very reliable. ...

What methods can I use to modify strings within JSX?

Within a JSX file, I am faced with the task of manipulating a particular string in a specific manner: Imagine that I have the following string assigned to medical_specialty = "Plastic Surgery" The objective is as follows: medical_specialty.replace(&apos ...