• HTML

    rating-stars.html html

    <rating-stars>
    	<fieldset>
    		<legend class="visually-hidden">Rate</legend>
    		<label>
    			<input type="radio" class="visually-hidden" name="rating" value="1">
    			<span class="label">☆</span>
    		</label>
    		<label>
    			<input type="radio" class="visually-hidden" name="rating" value="2">
    			<span class="label">☆</span>
    		</label>
    		<label>
    			<input type="radio" class="visually-hidden" name="rating" value="3">
    			<span class="label">☆</span>
    		</label>
    		<label>
    			<input type="radio" class="visually-hidden" name="rating" value="4">
    			<span class="label">☆</span>
    		</label>
    		<label>
    			<input type="radio" class="visually-hidden" name="rating" value="5">
    			<span class="label">☆</span>
    		</label>
    	</fieldset>
    </rating-stars>
    CSS

    rating-stars.css css

    rating-stars {
    	display: inline-block;
    	
    	& fieldset {
    		display: flex;
    		border: none;
            margin: 0;
            padding: 0;
    		justify-content: center;
    	}
    
    	& label {
    		display: block;
    		cursor: pointer;
    		width: var(--input-height);
    		height: var(--input-height);
    		color: var(--color-primary);
    		font-size: var(--font-size-l);
    		border-radius: var(--space-xs);
    		text-align: center;
    
    		&:hover,
    		&:focus-within {
    			color: var(--color-primary-hover);
    			background-color: rgba(0, 0, 0, 0.05);
    		}
    
    		&:active {
    			color: var(--color-primary-active);
    			background-color: rgba(0, 0, 0, 0.1);
    		}
    	}
    }
    TS

    rating-stars.ts ts

    import { UIElement, asInteger, setAttribute, setText } from '../../..'
    
    export class RatingStars extends UIElement<{ value: number }> {
    	static localName = 'rating-stars'
    	static observedAttributes = ['value']
    
    	init = {
            value: asInteger(),
        }
    
    	connectedCallback() {
            super.connectedCallback()
    
    		this.all('input')
    			.on('change', (_, index) => (e: Event) => {
    				e.stopPropagation()
    				this.set('value', index + 1)
    				this.self.emit('change-rating', index + 1)
    			})
    			.sync(setAttribute('checked', (_, index) => String(this.get('value') === index + 1)))
    		
    		this.all('.label')
    			.sync(setText((_, index) => index < this.get('value') ? '★' : '☆'))
        }
    }
    RatingStars.define()