Search code examples
reactjsstyled-components

Styling nested components using styled components


I've been using styled components for very little time.

At the moment I'm trying to do a style override on a nested element and I've having trouble understanding what I'm doing wrong.

So my struture is.

---------------------------Form.js---------------------------

import { FieldWrapper } from './FieldWrapper';

const Form = styled.form`
    /** my form styles **/
`;

const StyledFieldWrapper = styled(FieldWrapper)`
    /** my FieldWrapper styles **/

    input {
        /** input overrides **/
        padding-bottom: 0.8rem;
        height: 2rem;
        line-height: 2rem;
        box-sizing: content-box;
        background-color: pink !important; // added just for visibility
    }
`;

const MyForm = props => {
    return (
        <>
            <Form>
                <StyledFieldWrapper />
            </Form>
        </>
    );
}

export { MyForm }

---------------------------FieldWrapper.js---------------------------

const Div = styled.div`
    /** my div styles **/
`;

const Label = styled.label`
    /** my label styles **/
`;

const Input = styled.input`
    /** my input styles **/
`;

const FieldWrapper = props => {
    return (
        <Div>
            <Label>
                <Input />
            </Label>
        </Div>
    );
}

export { FieldWrapper }

Now what I expect to happen was that the styles in FieldWrapper.js would be overriden by the StyledFieldWrapper element in Form.js, this however does not happen and I have no idea why. I have overrides like this in the past and in this project. Also StyledFieldWrapper does not contain only overrides, it also has style of its own and I can't even see those.

Has anyone had a similar issue?

NOTE: I have tried to use the solution in Styling Nested Components in Styled-Components with no success.


Solution

  • EDIT:

    Since you want the styles to apply to a custom component, you also need to manually assign the className generated by styled-components to the top-level element of that component. Something like:

    const FieldWrapper = props => {
      return (
        <Div className={props.className}>
          <Label>
            <Input />
          </Label>
        </Div>
      );
    }
    

    The problem is likely related to CSS Specicifity, meaning that the original css styles defined in FieldWrapper has higher "importance" than the ones in Form. If you inspect your element you can probably see that both styles are applied, but the former has precendence over the latter.

    A way to solve that is to either use the !important rule to each of the input styles defined in your Form component. Another would be to add a class to <Input /> and define your styles as myClass.input. Basically anything that would increase the specicifity of the rules you want to apply.

    See the above link for more info of how to do that. Also check out Cascade and inheritance:

    Once you understand the fact that source order matters, at some point you will run into a situation where you know that a rule comes later in the stylesheet, but an earlier, conflicting, rule is applied. This is because that earlier rule has a higher specificity — it is more specific, and therefore is being chosen by the browser as the one that should style the element.

    As we saw earlier in this lesson, a class selector has more weight than an element selector, so the properties defined on the class will override those applied directly to the element.

    Something to note here is that although we are thinking about selectors, and the rules that are applied to the thing they select, it isn't the entire rule which is overwritten, only the properties which are the same.


    The amount of specificity a selector has is measured using four different values (or components), which can be thought of as thousands, hundreds, tens and ones — four single digits in four columns:

    1. Thousands: Score one in this column if the declaration is inside a style attribute, aka inline styles. Such declarations don't have selectors, so their specificity is always simply 1000.
    2. Hundreds: Score one in this column for each ID selector contained inside the overall selector.
    3. Tens: Score one in this column for each class selector, attribute selector, or pseudo-class contained inside the overall selector.
    4. Ones: Score one in this column for each element selector or pseudo-element contained inside the overall selector.

    Here is an example from MDN:

    /* specificity: 0101 */
    #outer a {
        background-color: red;
    }
    
    /* specificity: 0201 */
    #outer #inner a {
        background-color: blue;
    }