CSS Radio Button: Inner Circle Misaligned During Zoom/Dezoom

Discussão em 'Angular' iniciado por Fenix Fanel, Janeiro 17, 2025.

  1. Fenix Fanel

    Fenix Fanel Guest

    I am developing a custom Angular component for a radio button with specific design requirements:

    Outer circle size: Fixed at 16px x 16px. Inner circle size (checked state): Fixed at 8px x 8px. The inner circle must always remain perfectly centered inside the outer circle, even when zooming in or out (browser scaling). However, despite using various CSS techniques (e.g., Flexbox, transform: translate), the inner circle slightly shifts out of alignment when zooming or dezooming in the browser. I’m looking for a robust solution to ensure the inner circle stays centered.

    Technical Context: Framework: Angular 16. Code: Both HTML and SCSS are provided below. Goal: A CSS solution to ensure a stable alignment of the inner circle regardless of zoom levels.

    Here my HTML

    <span class="waldo-radio-button"
    [class.disabled]="disabled"
    [class.checked]="checked"
    [class.label-after]="labelPosition === 'after'"
    [class.label-bottom]="labelPosition === 'bottom'"
    (click)="onRadioChange($event)">

    <span class="radio-container">
    <span class="radio-button"
    [class.checked]="checked"
    [class.disabled]="disabled">

    <span class="inner-circle" *ngIf="checked"></span>
    </span>
    </span>

    <input type="radio"
    class="mat-radio-input"
    [id]="id"
    [name]="name"
    [value]="value"
    [checked]="checked"
    [disabled]="disabled"
    [required]="required"
    [attr.aria-describedby]="ariaDescribedby"
    [attr.aria-label]="ariaLabel"
    [attr.aria-labelledby]="ariaLabelledby" />

    <span class="mat-ripple mat-radio-ripple mat-focus-indicator">
    <span class="mat-ripple-element mat-radio-persistent-ripple"></span>
    </span>

    <label [for]="id" [style.color]="color">
    <ng-content></ng-content>
    </label>
    </span>


    And my SCSS

    .waldo-radio-button {
    display: inline-flex;
    align-items: center; /* Vertical alignment */
    cursor: pointer;
    position: relative;
    gap: 8px;

    &.disabled {
    cursor: not-allowed;

    .radio-button {
    border-color: var(--neutral-400);

    .inner-circle {
    background-color: var(--neutral-400);
    }
    }

    label {
    color: var(--neutral-400);
    }
    }

    &.checked {
    .radio-button {
    border: 2px solid var(--primary-600);

    .inner-circle {
    width: 8px;
    height: 8px;
    background-color: var(--primary-600);
    border-radius: 50%;
    transform: translateZ(0); /* To maintain sharp rendering */
    }

    &:hover {
    border-color: var(--primary-700);
    }

    &:active {
    border-color: var(--primary-800);
    }
    }
    }

    .radio-container {
    position: relative;
    width: 16px; /* Outer circle size */
    height: 16px;
    display: flex;
    justify-content: center; /* Horizontal centering */
    align-items: center; /* Vertical centering */
    }

    .radio-button {
    width: 16px;
    height: 16px;
    border: 2px solid var(--neutral-500); /* Default border color */
    border-radius: 50%; /* Perfect circle */
    display: flex;
    justify-content: center;
    align-items: center; /* Center the inner circle */
    transition: border-color 0.2s ease;

    .inner-circle {
    width: 8px; /* Inner circle size */
    height: 8px;
    background-color: transparent; /* Initially invisible */
    border-radius: 50%; /* Perfect circle */
    transform: scale(0); /* Hidden by default */
    transition: transform 0.2s ease, background-color 0.2s ease;
    }
    }

    input[type='radio'] {
    position: absolute;
    opacity: 0;
    pointer-events: none;
    }

    label {
    color: var(--neutral-700);
    cursor: pointer;
    display: inline-block;
    }
    }


    What I’ve Tried: Flexbox: Used in .radio-container and .radio-button to center the inner circle. Transformations: Applied transform: scale and translateZ(0) to stabilize rendering. Fixed Sizes: Set fixed dimensions for both the outer circle (16px) and the inner circle (8px).

    Continue reading...

Compartilhe esta Página