Avoid wrapping element style-overrides

Hi. I love the Ionic Framework!

I am trying to combine local styling with the platform defaults in a good way. The question goes for this kind of nesting of components in general, but using one specific case to as an example here.

I have styled the ion-button component (IonButton in React) by setting a lot of css variables (like --border-radius etc). Simplified example:

ion-button {
  --border-radius: 8px;
}

That works very well, but once I put the component inside a IonButtons to place it in a toolbar (like the documentation suggests), it sets a different border-radius (and other css props). I found this source styling causing it:

.sc-ion-buttons-md-s ion-button:not(.button-round) {
    --border-radius: 2px;
}

I tried a few things to avoid this and get it to use the styles I set on the button (since I want the button to have the same styles inside the header as the default button style), like:

  • Setting styles with a class instead of the element (.button instead of ion-button, BEM-style)
  • Nesting the style rules in buttons like this: ion-buttons ion-button { --border-radius: 8px; }}
  • Using the not-selector in case that would override the above rule, like this: ion-buttons ion-button:not(.button-round)

Nothing seems to work except setting !important which is not a good solution in my experience. Hardcoding the same rule (.sc-ion-buttons-md-s ion-button:not(.button-round)) in my styling seems wrong as well, as it ties it closely to the implementation of the ionic inner features and that might change.

So, how should these overriding issues best be solved?

You are unable to style the button using normal css is because its wrapped insde a shadow-root of that element

Here is a sample
shadow-root

Inorder to style such shadow root element you will need to use JS to first access it and programatically set the style or class on that element inside the shadow root

@nvispute Thanks for responding.

Styling the elements work well as long as I set the style vars, like this:

ion-button {
  --border-radius: 8px;
}

or

ion-button::part(native) {
  border-radius: 8px;
}

The problem is that some css (like the line listed in the first post) from the ionic framework itself overrides it when I have put that element inside another one. And I want to avoid having to go to “hacks” like

ion-buttons ion-botton {
  border-radius: 8px !important;
}

When the default styles need to work differently when the element is inside another element, I would think using nesting like this in the framework would be better, so that nesting can be overridden in implementations as well:

ion-buttons ion-button {
  --border-radius: 2px;
}

but instead, they set it on a specific class (see first post) that is problematic to override.

Referring to your original post what i understand is that you a neat looking custom button which you wish to use under the toolbar…
But the Problem is that Toolbar has shadow-root and then you need to wrap your custom button inside of Ion-buttons which is a Scoped component. On top of that the Ion-Button too is has Shadow-root

Now as per the documentation inorder to leak the style inside a shadow-root element you will need to use ::part() pesudo element but the if you use it on your custom button

ion-button::part(native) {
  border-radius: 8px;
}

it will not be applied becasue the button itself is inside of the toolbar shadow-root

Thus the solution for this issue might be / should be using ::part() on toolbar so that it leak to the inner components

I think this way right / correct way to maintain specificity of each individual component for local level styling. i mean this shall allow us to target that very specific element in the dom and then not worry of that stlying leaking out to other similar tags…

That worked, thank you.

But I still don’t fully understand if that was the problem. Because, I have customized ion-button like by setting the variable:

ion-button {
  --border-radius: 8px;
}

that works for buttons, except the ones inside ion-buttons. This CSS-rule from ionic overrides it (found inspecting in the browser):

.sc-ion-buttons-md-s ion-button:not(.button-round) {
    --border-radius: 2px;
}

If I add this rule:

ion-buttons ion-button {
  --border-radius: 12px;
}

and disable the 2px-rule in the Chrome-inspector by disabling the checkbox this way:

Then, the rule (12px) is the one controlling the border-radius. So it seems setting ionic-variables based on nesting the elements like that work (even if they are inside shadow-roots), however the 2px-css-rule override them. It seems a little bit strange to have to add this directly on the part(native) just because a class-based (.sc-ion-buttons-md-s) rule override variables set in a normal manner (ion-buttons ion.button {}).

ion-buttons is a scoped component which means the style set on ion-button are as good as setting the styling in :root. Also Ionic css var are set on :root level they only provide the values to the rules…

In order to understand this completely, you will need to understand the CSS specificity of the components you are nesting…

MDN :scoped - css

CSS Specificity - W3School