How to style a radio button to look like a regular Learn More Button
I am designing an app in Unqork. The issue I have is how to shape the radio button to look like the regular Learn More button. I only intend to style the radio button instead of making it into a multiple choice. The style of the regular button is
.abc-quote-option-cta < bottom: 38px; left: 0; right: 0; padding-top: 24px; position: absolute; >.abc-quote-option-cta:before < background-color: #979797; content: ''; display: block; height: 1px; left: 62px; opacity: .36; position: absolute; right: 56px; top: 0; >.abc-quote-option-cta .btn.btn-primary < border-radius: 6px; border: solid 2px #01a7e1; background-color: #FFFFFF; border-radius: 6px; color: #01a7e1; display: block; font-size: 1.8rem; line-height: 1.44; margin: 0 auto; padding: 10px; text-align: center; transition: background-color .25s ease, width .13s ease-in, color .18s ease-out ; width: 188px; >.abc-quote-option-cta .btn.btn-primary:hover, .abc-quote-option-cta .btn.btn-primary:focus
How to style a radio button with CSS
Creating a user-friendly and visually appealing interface is a crucial task for any software developer. One of the common elements that you may need to style in your user interface design are radio buttons. In this tutorial, we will walk you through the process of styling a radio button using CSS.
Whether you’re building a form or a survey, a radio button is a great way to present options to users. However, the default radio buttons provided by browsers are often not visually appealing and may not fit your design scheme. That’s why knowing how to style them with CSS is a useful skill to possess.
Step 1: Create the HTML structure
Let’s start by creating a simple HTML structure for our radio button. We’ll create a form with two radio options.
Step 2: Style the Radio Button with CSS
Now that we have our basic structure, let’s move on to the CSS part. We’ll start by hiding the default radio button using the ‘appearance’ CSS property, and then we’ll create our custom radio button.
.custom-radio input[type=’radio’] < appearance: none; width: 20px; height: 20px; border: 1px solid #999; border-radius: 50%; outline: none; box-shadow: 0 0 5px #999; transition: box-shadow 0.3s ease; >.custom-radio input[type=’radio’]:before < content: ''; display: block; width: 60%; height: 60%; margin: 20% auto; border-radius: 50%; >.custom-radio input[type=’radio’]:checked:before
With this CSS styling, we’ve created a new radio button with a custom appearance. The ‘appearance: none;’ line removes the default styling of the radio button. The ‘width’ and ‘height’ properties set the size of the button, and the ‘border’, ‘border-radius’, ‘outline’, and ‘box-shadow’ properties style the border and shadow of the button. The ‘:before’ pseudo-element creates a circle inside the button, and the ‘:checked:before’ changes the color of this circle when the button is selected.
That’s it! You have successfully styled a radio button with CSS. Remember, CSS is a powerful tool that allows you to customize your webpage to fit your specific design. Don’t be afraid to experiment with different styles and properties.
If you find this task overwhelming or you’d rather focus on the logic of your application, remember that you can always hire remote HTML/CSS developers.
RadioButton
The .NET Multi-platform App UI (.NET MAUI) RadioButton is a type of button that allows users to select one option from a set. Each option is represented by one radio button, and you can only select one radio button in a group. By default, each RadioButton displays text:
However, on some platforms a RadioButton can display a View, and on all platforms the appearance of each RadioButton can be redefined with a ControlTemplate:

RadioButton defines the following properties:
- Content , of type object , which defines the string or View to be displayed by the RadioButton.
- IsChecked , of type bool , which defines whether the RadioButton is checked. This property uses a TwoWay binding, and has a default value of false .
- GroupName , of type string , which defines the name that specifies which RadioButton controls are mutually exclusive. This property has a default value of null .
- Value , of type object , which defines an optional unique value associated with the RadioButton.
- BorderColor , of type Color, which defines the border stroke color.
- BorderWidth , of type double , which defines the width of the RadioButton border.
- CharacterSpacing , of type double , which defines the spacing between characters of any displayed text.
- CornerRadius , of type int , which defines the corner radius of the RadioButton.
- FontAttributes , of type FontAttributes , which determines text style.
- FontAutoScalingEnabled , of type bool , which defines whether an app’s UI reflects text scaling preferences set in the operating system. The default value of this property is true .
- FontFamily , of type string , which defines the font family.
- FontSize , of type double , which defines the font size.
- TextColor , of type Color, which defines the color of any displayed text.
- TextTransform , of type TextTransform , which defines the casing of any displayed text.
These properties are backed by BindableProperty objects, which means that they can be targets of data bindings, and styled.
RadioButton also defines a CheckedChanged event that’s raised when the IsChecked property changes, either through user or programmatic manipulation. The CheckedChangedEventArgs object that accompanies the CheckedChanged event has a single property named Value , of type bool . When the event is raised, the value of the CheckedChangedEventArgs.Value property is set to the new value of the IsChecked property.
RadioButton grouping can be managed by the RadioButtonGroup class, which defines the following attached properties:
- GroupName , of type string , which defines the group name for RadioButton objects in an ILayout .
- SelectedValue , of type object , which represents the value of the checked RadioButton object within an ILayout group. This attached property uses a TwoWay binding by default.
For more information about the GroupName attached property, see Group RadioButtons. For more information about the SelectedValue attached property, see Respond to RadioButton state changes.
Create RadioButtons
The appearance of a RadioButton is defined by the type of data assigned to the RadioButton.Content property:
- When the RadioButton.Content property is assigned a string , it will be displayed on each platform, horizontally aligned next to the radio button circle.
- When the RadioButton.Content is assigned a View, it will be displayed on supported platforms (iOS, Windows), while unsupported platforms will fallback to a string representation of the View object (Android). In both cases, the content is displayed horizontally aligned next to the radio button circle.
- When a ControlTemplate is applied to a RadioButton, a View can be assigned to the RadioButton.Content property on all platforms. For more information, see Redefine RadioButton appearance.
Display string-based content
A RadioButton displays text when the Content property is assigned a string :
In this example, RadioButton objects are implicitly grouped inside the same parent container. This XAML results in the appearance shown in the following screenshot:

Display arbitrary content
On iOS and Windows, a RadioButton can display arbitrary content when the Content property is assigned a View:
In this example, RadioButton objects are implicitly grouped inside the same parent container. This XAML results in the appearance shown in the following screenshot:

On Android, RadioButton objects will display a string-based representation of the View object that’s been set as content.
When a ControlTemplate is applied to a RadioButton, a View can be assigned to the RadioButton.Content property on all platforms. For more information, see Redefine RadioButton appearance.
Associate values with RadioButtons
Each RadioButton object has a Value property, of type object , which defines an optional unique value to associate with the radio button. This enables the value of a RadioButton to be different to its content, and is particularly useful when RadioButton objects are displaying View objects.
The following XAML shows setting the Content and Value properties on each RadioButton object:
In this example, each RadioButton has an Image as its content, while also defining a string-based value. This enables the value of the checked radio button to be easily identified.
Group RadioButtons
Radio buttons work in groups, and there are three approaches to grouping radio buttons:
- Place them inside the same parent container. This is known as implicit grouping.
- Set the GroupName property on each radio button in the group to the same value. This is known as explicit grouping.
- Set the RadioButtonGroup.GroupName attached property on a parent container, which in turn sets the GroupName property of any RadioButton objects in the container. This is also known as explicit grouping.
RadioButton objects don’t have to belong to the same parent to be grouped. They are mutually exclusive provided that they share a group name.
Explicit grouping with the GroupName property
The following XAML example shows explicitly grouping RadioButton objects by setting their GroupName properties:
In this example, each RadioButton is mutually exclusive because it shares the same GroupName value.
Explicit grouping with the RadioButtonGroup.GroupName attached property
The RadioButtonGroup class defines a GroupName attached property, of type string , which can be set on a Layout object. This enables any layout to be turned into a radio button group:
In this example, each RadioButton in the StackLayout will have its GroupName property set to colors , and will be mutually exclusive.
When an ILayout object that sets the RadioButtonGroup.GroupName attached property contains a RadioButton that sets its GroupName property, the value of the RadioButton.GroupName property will take precedence.
Respond to RadioButton state changes
A radio button has two states: checked or unchecked. When a radio button is checked, its IsChecked property is true . When a radio button is unchecked, its IsChecked property is false . A radio button can be cleared by tapping another radio button in the same group, but it cannot be cleared by tapping it again. However, you can clear a radio button programmatically by setting its IsChecked property to false .
Respond to an event firing
When the IsChecked property changes, either through user or programmatic manipulation, the CheckedChanged event fires. An event handler for this event can be registered to respond to the change:
The code-behind contains the handler for the CheckedChanged event:
void OnColorsRadioButtonCheckedChanged(object sender, CheckedChangedEventArgs e) < // Perform required operation >
The sender argument is the RadioButton responsible for this event. You can use this to access the RadioButton object, or to distinguish between multiple RadioButton objects sharing the same CheckedChanged event handler.
Respond to a property change
The RadioButtonGroup class defines a SelectedValue attached property, of type object , which can be set on an ILayout object. This attached property represents the value of the checked RadioButton within a group defined on a layout.
When the IsChecked property changes, either through user or programmatic manipulation, the RadioButtonGroup.SelectedValue attached property also changes. Therefore, the RadioButtonGroup.SelectedValue attached property can be data bound to a property that stores the user’s selection:
In this example, the value of the RadioButtonGroup.GroupName attached property is set by the GroupName property on the binding context. Similarly, the value of the RadioButtonGroup.SelectedValue attached property is set by the Selection property on the binding context. In addition, the Selection property is updated to the Value property of the checked RadioButton.
RadioButton visual states
RadioButton objects have Checked and Unchecked visual states that can be used to initiate a visual change when a RadioButton is checked or unchecked.
The following XAML example shows how to define a visual state for the Checked and Unchecked states:
In this example, the implicit Style targets RadioButton objects. The Checked VisualState specifies that when a RadioButton is checked, its TextColor property will be set to green with an Opacity value of 1. The Unchecked VisualState specifies that when a RadioButton is in a unchecked state, its TextColor property will be set to red with an Opacity value of 0.5. Therefore, the overall effect is that when a RadioButton is unchecked it’s red and partially transparent, and is green without transparency when it’s checked:

For more information about visual states, see Visual states.
Redefine RadioButton appearance
By default, RadioButton objects use handlers to utilize native controls on supported platforms. However, RadioButton visual structure can be redefined with a ControlTemplate, so that RadioButton objects have an identical appearance on all platforms. This is possible because the RadioButton class inherits from the TemplatedView class.
The following XAML shows a ControlTemplate that can be used to redefine the visual structure of RadioButton objects:
In this example, the root element of the ControlTemplate is a Border object that defines Checked and Unchecked visual states. The Border object uses a combination of Grid, Ellipse, and ContentPresenter objects to define the visual structure of a RadioButton. The example also includes an implicit style that will assign the RadioButtonTemplate to the ControlTemplate property of any RadioButton objects on the page.
The ContentPresenter object marks the location in the visual structure where RadioButton content will be displayed.
The following XAML shows RadioButton objects that consume the ControlTemplate via the implicit style:
In this example, the visual structure defined for each RadioButton is replaced with the visual structure defined in the ControlTemplate, and so at runtime the objects in the ControlTemplate become part of the visual tree for each RadioButton. In addition, the content for each RadioButton is substituted into the ContentPresenter defined in the control template. This results in the following RadioButton appearance:

For more information about control templates, see Control templates.
Disable a RadioButton
Sometimes an app enters a state where a RadioButton being checked is not a valid operation. In such cases, the RadioButton can be disabled by setting its IsEnabled property to false .
Collaborate with us on GitHub
The source for this content can be found on GitHub, where you can also create and review issues and pull requests. For more information, see our contributor guide.
.NET MAUI feedback
The .NET Multi-platform App UI (.NET MAUI) documentation is open source. Please provide feedback here.
Feedback
Submit and view feedback for
Pure CSS Custom Styled Radio Buttons
This is episode #18 in a series examining modern CSS solutions to problems Stephanie Eckles has been solving over the last 15+ years as a front-end dev.
Table of Contents
Using a combination of the following properties, we can create custom, accessible, cross-browser, theme-able, scalable radio buttons in pure CSS:
- currentColor for theme-ability
- em units for relative sizing
- appearance: none for full restyling access
- CSS grid layout to align the input and label
Head’s up: A lot of these styles overlap with the episode on custom checkbox styles which you might be interested in reading next!
Now available: my egghead video course Accessible Cross-Browser CSS Form Styling. You’ll learn to take the techniques described in this tutorial to the next level by creating a themable form design system to extend across your projects.
Radio Button HTML
There are two appropriate ways to layout radio buttons in HTML.
The first wraps the input within the label . This implicitly associates the label with the input that its labeling, and also increases the hit area to select the radio.
label> input type="radio" name="radio" /> Radio label text label>
The second is to have the input and label be siblings and use the for attribute set to the value of the radio’s id to create the association.
input type="radio" name="radio" id="radio1" /> label for="radio1">Radio label textlabel>
Our technique will work with either setup, although we’re going to select the wrapping label method to prevent including an extra div.
The base HTML for our demo including classes and two radios — necessary to test :checked vs. un-checked states — is the following:
label class="form-control"> input type="radio" name="radio" /> Radio label> label class="form-control"> input type="radio" name="radio" /> Radio - checked label>
For groups of radio buttons, it is also necessary to provide the same name attribute.
Here’s how the native HTML elements in Chrome appear:

Common Issues with Native Radio Buttons
The primary issue that causes developers to seek a custom styling solution for radio buttons is the variance in their appearance between browsers which is increased when including mobile browsers as well.
As an example, here are radio buttons as shown on Mac versions of Firefox (left), Chrome (middle), and Safari (right):

Our solution will accomplish the following goals:
- scale with the font-size provided to the label
- gain the same color as provided to the label for ease of theme-ability
- achieve a consistent, cross-browser design style, including :focus state
- maintain keyboard and color contrast accessibility
If your primary goal is modifying the :checked state color, you may be interested in learning more about the upcoming accent-color property from Michelle Barker’s overview.
Theme Variable and box-sizing Reset
There are two base CSS rules that must be placed first in our cascade.
First, we create a custom variable called —color which we will use as a simple way to easily theme our radio buttons.
:root --form-control-color: rebeccapurple; >
Next, we use the universal selector to reset the box-sizing method used to border-box . This means that padding and border will be included in the calculation of any elements computed final size instead of increasing the computed size beyond any set dimensions.
*, *:before, *:after box-sizing: border-box; >
Label Styles
Our label uses the class of .form-control . The base styles we’ll include here are font styles. Recall from earlier that the font-size will not yet have an effect on the visual size of the radio input .
CSS for «.form-control font styles»
.form-control font-family: system-ui, sans-serif; font-size: 2rem; font-weight: bold; line-height: 1.1; >
Radio Radio — checked
We’re using an abnormally large font-size just to emphasize the visual changes for purposes of the tutorial demo.
Our label is also the layout container for our design, and we’re going to set it up to use CSS grid layout to take advantage of gap .
CSS for «.form-control grid layout»
.form-control font-family: system-ui, sans-serif; font-size: 2rem; font-weight: bold; line-height: 1.1; display: grid; grid-template-columns: 1em auto; gap: 0.5em; >
Radio Radio — checked
Custom Radio Button Style
Ok, this is the part you came here for!
The original version of this tutorial demonstrated use of extra elements to achieve the desired effect. Thanks to improved support of appearance: none and with appreciation to Scott O’Hara’s post on styling radio buttons and checkboxes, we can rely on pseudo elements instead!
Join my newsletter for article updates, CSS tips, and front-end resources!
Step 1: Hide the Native Radio Input
We need to hide the native radio input, but keep it technically accessible to enable proper keyboard interaction and also to maintain access to the :focus state.
To accomplish this, we only need to set appearance: none . This removes nearly all inherited browser styles and gives us access to styling the input’s pseudo elements. Notice we have two additional properties to complete the reset.
CSS for «hiding the native radio input»
input[type="radio"] /* Add if not using autoprefixer */ -webkit-appearance: none; appearance: none; /* For iOS < 15 to remove gradient background */background-color: #fff; /* Not removed via appearance */ margin: 0; >
Radio Radio — checked
Worried about support? This combination of using appearance: none and the ability to style the input’s pseudo elements has been supported since 2017 in Chrome, Safari, and Firefox, and in Edge since their switch to Chromium in May 2020.
Step 2: Custom Unchecked Radio Styles
For our custom radio, we’ll update box styles on the base input element. This includes inheriting the font styles to ensure the use of em produces the desired sizing outcome, as well as using currentColor to inherit any update on the label’s color.
We use em for the width , height , and border-width value to maintain the relative appearance. Good ole border-radius: 50% finishes the expected appearance by rendering the element as a circle.
CSS for «custom unchecked radio styles»
input[type="radio"] appearance: none; background-color: #fff; margin: 0; font: inherit; color: currentColor; width: 1.15em; height: 1.15em; border: 0.15em solid currentColor; border-radius: 50%; > .form-control + .form-control margin-top: 1em; >
Radio Radio — checked
Finally, we slid in a little style to provide some space between our radios by applying margin-top with the help of the adjacent sibling combinator;
Step 3: Improve Input vs. Label Alignment
If you’ve worked with grid or flexbox, your instinct right now might be to apply align-items: center to optically tune the alignment of the input in relation to the label text.
But what if the label is long enough to become broken across multiple lines? In that case, alignment along horizontal center may be undesirable.
Instead, let’s make adjustments so the input stays horizontally centered in relation to the first line of the label text.
On our input, we’ll use transform to nudge the element up. This is a bit of a magic number, but as a starting point this value is half the size of the applied border.
CSS for «improve input vs. label alignment»
input[type="radio"] appearance: none; background-color: #fff; margin: 0; font: inherit; color: currentColor; width: 1.15em; height: 1.15em; border: 0.15em solid currentColor; border-radius: 50%; transform: translateY(-0.075em); >
Radio Radio — checked
And with that our alignment is complete and functional for both single-line and multi-line labels.
Step 4: The :checked State
It’s now time to bring in our ::before pseudo element which will be styled in order to represent the :checked state.
The :checked naming convention may be a little confusing here, but it is a CSS selector that is available for both radio buttons and checkboxes.
We first need to change the display behavior of the input to use grid:
input[type="radio"] /* . existing styles */ display: grid; place-content: center; >
This is the quickest way to align the :before to the horizontal and vertical center of our custom control.
Then, we create the :before element, including a transition and using transform hide it with scale(0) :
input[type="radio"]::before content: ""; width: 0.65em; height: 0.65em; border-radius: 50%; transform: scale(0); transition: 120ms transform ease-in-out; box-shadow: inset 1em 1em var(--form-control-color); >
Use of box-shadow instead of background-color will enable the state of the radio to be visible when printed (h/t Alvaro Montoro).
Finally, when the input is :checked , we make it visible with scale(1) with a nicely animated result thanks to the transition . Be sure to click between the radios to see the animation!
CSS for «:checked state styles»
input[type="radio"] /* . existing styles */ display: grid; place-content: center; > input[type="radio"]::before content: ""; width: 0.65em; height: 0.65em; border-radius: 50%; transform: scale(0); transition: 120ms transform ease-in-out; box-shadow: inset 1em 1em var(--form-control-color); > input[type="radio"]:checked::before transform: scale(1); >
Radio Radio — checked
High Contrast Themes and Forced Colors
One more state we need to ensure our radio responds to is what you may hear referred to as «Windows High Contrast Mode» (WHCM). In this mode, the user’s operating system swaps out color-related properties for a reduced palette which is an incoming part of the CSS spec called «forced-colors».
In this mode, our box-shadow is completely removed, leaving these users without an indicator of the checked state.
Fortunately, resolving this involves adding just one extra property. We’ll provide a background-color , which is normally removed in forced-colors mode, but will be retained if we use one of the defined forced colors. In this case, we’re selecting CanvasText which will match the regular body text color.
Due to the style stacking order, our box-shadow that we’ve themed for use in regular mode is actually visuallly placed over the background-color , meaning we can use both without any further modifications.
CSS for «supporting forced-colors»
input[type="radio"]::before /* . existing styles */ /* Windows High Contrast Mode */ background-color: CanvasText; >
Radio Radio — checked
Step 5: The :focus State
Depending on your browser, you may already be seeing some kind of a focus style provided as an outline . We’ll add just a tiny bit of customization to make it match our input’s color, and provide some space from the input by using outline-offset .
This is a simplification from the earlier version of this tutorial which used box-shadow . Now, evergreen browsers all support outline which follows border-radius , removing an excuse not to just use the outline !
Remember: :focus is a temporary state, but it’s very important that it is highly visible to ensure the accessibility of your form controls and other interactive elements.
CSS for «:focus state styles»
input[type="radio"]:focus outline: max(2px, 0.15em) solid currentColor; outline-offset: max(2px, 0.15em); >
Radio Radio — checked
And with that, the essential styles for a custom radio button are complete!
Experimental: Using :focus-within to Style the Label Text
Since the label is not a sibling of the native input, we can’t use the :focus state of the input to style it.
An upcoming pseudo selector is :focus-within , and one feature is that it can apply styles to elements that contain an element which has received focus.
For now, any critial usage of :focus-within requires a polyfill, so the following styles should be considered an enhancement and not relied on as the only way to provide a visual indication of focus.
We’ll test for focus by adding a rule for :focus-within on the label ( .form-control ). This means when the native input — which is a child and therefore «within» the label — receives focus, we can style any element within the label while focus is active.
CSS for «experimental :focus-within styles»
.form-control:focus-within color: var(--form-control-color); >
Radio Radio — checked
Demo
Here is the solution altogether in a CodePen that you can fork and experiment with further.
By Stephanie Eckles (@5t3ph)
Check out the custom checkbox styling to also learn how to extend styles to the :disabled state, and see how to work with clip-path as a :checked indicator.
What to Read Next
3 Popular Website Heroes Created With CSS Grid Layout This episode explores creating website heroes — aka ‘headers’ — by using CSS grid and a unique way to apply grid template areas that you can use to replace older methods that used absolute positioning.
Equal Height Elements: Flexbox vs. Grid Review solutions using both Flexbox and CSS grid and learn when you might choose one over the other.
CSS Tips in Your Inbox Join my newsletter for article updates, CSS tips, and front-end resources! Newsletter Signup
CSS-Only Full-Width Responsive Images 2 Ways Let’s look at how to use `background-size` and `object-fit` for similar full-width image effects, and learn when to select one over the other.
