ADDED: Mozilla styles for slider

FIXED: Some typography issues and a ripple effect bug that appears in Firefox
And some changes, about which I forget
This commit is contained in:
doryan04 2024-02-19 23:43:39 +04:00
commit cb04ae521a
30 changed files with 1221 additions and 323 deletions

View File

@ -9,24 +9,25 @@ This repository is including and will be including components, enumerates in tab
- [x] Icon - [x] Icon
- [x] FAB - [x] FAB
- [X] Radio - [X] Radio
- [ ] Segmented (WIP) - [X] Segmented
- [X] Checkbox - [X] Checkbox
- [x] Text fields - [x] Text fields
- [X] Switches - [X] Switches
- [ ] Chips
- [x] Icon - [x] Icon
- [x] Ripple Effect - [x] Ripple Effect
- [X] Dividers - [X] Dividers
- [x] Badges - [x] Badges
- [ ] Select field
- [ ] Bottom sheets
- [X] Cards - [X] Cards
- [X] Typography
- [ ] Chips
- [ ] Select field (WIP)
- [ ] Bottom sheets
- [ ] Menus - [ ] Menus
- [ ] Navigation - [ ] Navigation
- [ ] Bars - [ ] Bars
- [ ] Drawer - [ ] Drawer
- [ ] Rail - [ ] Rail
- [ ] Sliders - [ ] Sliders (WIP)
- [ ] Snackbar - [ ] Snackbar
- [ ] Tabs - [ ] Tabs
- [ ] Bottom sheets - [ ] Bottom sheets

View File

@ -1,7 +1,7 @@
import React from 'react'; import React from 'react';
import testImage1 from './test-images/test-image-1.jpg'; import testImage1 from './test-images/test-image-1.jpg';
import { Card } from '../src/primitive-components/card/card'; import { Card } from '../src/primitive-components/card/card';
import {Checkbox, Radio} from '../src/primitive-components/components'; import { Checkbox, IconButton } from '../src/primitive-components/components';
import { CardBody } from '../src/primitive-components/card/card-body'; import { CardBody } from '../src/primitive-components/card/card-body';
import { CardMedia } from '../src/primitive-components/card/card-media'; import { CardMedia } from '../src/primitive-components/card/card-media';
import { CardFooter } from '../src/primitive-components/card/card-footer'; import { CardFooter } from '../src/primitive-components/card/card-footer';
@ -9,7 +9,6 @@ import { CardHeader } from '../src/primitive-components/card/card-header';
import { Typography } from '../src/primitive-components/typography/typography'; import { Typography } from '../src/primitive-components/typography/typography';
import { CardActionArea } from '../src/primitive-components/card/card-action-area'; import { CardActionArea } from '../src/primitive-components/card/card-action-area';
import { Slider } from '../src/primitive-components/input-components/slider/slider'; import { Slider } from '../src/primitive-components/input-components/slider/slider';
import { Button } from '../src/primitive-components/button-components/button/button';
import { SegmentButton } from '../src/primitive-components/button-components/segmented-buttons/segment-button'; import { SegmentButton } from '../src/primitive-components/button-components/segmented-buttons/segment-button';
import { SegmentedButtons } from '../src/primitive-components/button-components/segmented-buttons/segmented-buttons'; import { SegmentedButtons } from '../src/primitive-components/button-components/segmented-buttons/segmented-buttons';
@ -25,15 +24,22 @@ export default function Page() {
padding: '8px', padding: '8px',
}} }}
> >
<div style={{ display: 'flex', gap: '8px', maxWidth: '1024px' }}> <div
style={{ display: 'flex', gap: '8px', maxWidth: '1024px' }}
>
<Card variant={'outlined'}> <Card variant={'outlined'}>
<CardHeader> <CardHeader>
<Typography.h3> Header-3 </Typography.h3> <Typography role={'headline'} size={'large'}>
Welcome to Material You for Next.js!
</Typography>
<Typography role={'body'} size={'large'}>
{"It's UI kit for fast frontend development!"}
</Typography>
</CardHeader> </CardHeader>
<CardActionArea> <CardActionArea>
<CardMedia src={testImage1.src} type={'img'} /> <CardMedia src={testImage1.src} type={'img'} />
<CardBody> <CardBody>
<p> <Typography role={'body'} size={'large'}>
Lorem ipsum dolor sit amet, consecrate Lorem ipsum dolor sit amet, consecrate
adipiscing elit, sed do eiusmod tempor adipiscing elit, sed do eiusmod tempor
incididunt ut labore et dolore magna aliqua. incididunt ut labore et dolore magna aliqua.
@ -45,16 +51,20 @@ export default function Page() {
Excepteur sint occaecat cupidatat non Excepteur sint occaecat cupidatat non
proident, sunt in culpa qui officia deserunt proident, sunt in culpa qui officia deserunt
mollit anim id est laborum. mollit anim id est laborum.
</p> </Typography>
</CardBody> </CardBody>
</CardActionArea> </CardActionArea>
<CardFooter> <CardFooter>
<div className={'flex flex-row gap-3'}> <div className={'flex flex-row gap-3'}>
<Button icon={'add'}>Label 1</Button> <IconButton
<Button icon={'add'} iconPlace={'right'}> icon={'add'}
Label 2 toggled={{
</Button> selected: 'settings',
<SegmentedButtons> unselected: 'add',
}}
variant={'filled'}
/>
<SegmentedButtons toggled={true}>
<SegmentButton <SegmentButton
fillIcon={1} fillIcon={1}
icon={'change_history'} icon={'change_history'}
@ -73,8 +83,13 @@ export default function Page() {
</SegmentButton> </SegmentButton>
</SegmentedButtons> </SegmentedButtons>
<Checkbox /> <Checkbox />
<Radio /> <Slider
<Slider /> defaultValue={0}
max={100}
min={0}
options={[0, 10, 20, 100]}
/>
<Slider defaultValue={0} max={100} min={0} />
</div> </div>
</CardFooter> </CardFooter>
</Card> </Card>

View File

@ -5,6 +5,7 @@ import { ButtonProps } from './button.types';
import { bool, oneOf, string } from 'prop-types'; import { bool, oneOf, string } from 'prop-types';
import { ButtonLayout } from '../button-layout/button-layout'; import { ButtonLayout } from '../button-layout/button-layout';
import { IconWrapper } from '../../icon/icon-wrapper'; import { IconWrapper } from '../../icon/icon-wrapper';
import { Typography } from '../../typography/typography';
/** /**
* Button component * Button component
@ -32,7 +33,13 @@ export const Button = forwardRef<HTMLButtonElement, ButtonProps>(
ref={ref} ref={ref}
> >
<IconWrapper icon={icon} iconPlace={iconPlace}> <IconWrapper icon={icon} iconPlace={iconPlace}>
<span className={'label-large'}>{props.children}</span> <Typography
className={'label-large'}
role={'label'}
size={'large'}
>
{props.children}
</Typography>
</IconWrapper> </IconWrapper>
</ButtonLayout> </ButtonLayout>
), ),

View File

@ -1,9 +1,10 @@
'use client'; 'use client';
import { forwardRef } from 'react';
import { Icon } from '../../components';
import { FABProps } from './fab.types'; import { FABProps } from './fab.types';
import { Icon } from '../../components';
import React, { forwardRef } from 'react';
import { bool, oneOf, string } from 'prop-types'; import { bool, oneOf, string } from 'prop-types';
import { Typography } from '../../typography/typography';
import { ButtonLayout } from '../button-layout/button-layout'; import { ButtonLayout } from '../button-layout/button-layout';
/** /**
@ -21,6 +22,7 @@ const sizes = {
export const FAB = forwardRef<HTMLButtonElement, FABProps>( export const FAB = forwardRef<HTMLButtonElement, FABProps>(
( (
{ {
children,
icon = 'edit', icon = 'edit',
className = '', className = '',
size = 'default', size = 'default',
@ -43,7 +45,13 @@ export const FAB = forwardRef<HTMLButtonElement, FABProps>(
{icon} {icon}
</Icon> </Icon>
{size === 'extended' ? ( {size === 'extended' ? (
<span className={'label-large'}>{props.children}</span> <Typography
className={'label-large'}
role={'label'}
size={'large'}
>
{children}
</Typography>
) : ( ) : (
<></> <></>
)} )}

View File

@ -3,10 +3,8 @@
import { string } from 'prop-types'; import { string } from 'prop-types';
import React, { forwardRef, useState } from 'react'; import React, { forwardRef, useState } from 'react';
import { IconWrapper } from '../../icon/icon-wrapper'; import { IconWrapper } from '../../icon/icon-wrapper';
import { Typography } from '../../typography/typography';
import { SegmentedButton } from './segmented-buttons.types'; import { SegmentedButton } from './segmented-buttons.types';
import { ButtonLayout } from '../button-layout/button-layout';
import { ButtonLayoutProps } from '../button-layout/button-layout.types';
export const SegmentButton = forwardRef< export const SegmentButton = forwardRef<
HTMLButtonElement, HTMLButtonElement,
ButtonLayoutProps & SegmentedButton ButtonLayoutProps & SegmentedButton
@ -19,6 +17,7 @@ export const SegmentButton = forwardRef<
weight, weight,
svgSize, svgSize,
fillIcon, fillIcon,
children,
iconSize, iconSize,
opticalSize, opticalSize,
toggled = false, toggled = false,
@ -58,13 +57,22 @@ export const SegmentButton = forwardRef<
type={type} type={type}
weight={weight} weight={weight}
> >
<span className={'label-large'}>{props.children}</span> <Typography
className={'label-large'}
role={'label'}
size={'large'}
>
{children}
</Typography>
</IconWrapper> </IconWrapper>
<span className={'m3 m3-button-segment-state-layer'} /> <span className={'m3 m3-button-segment-state-layer'} />
</ButtonLayout> </ButtonLayout>
); );
}, },
); );
import { ButtonLayout } from '../button-layout/button-layout';
import { ButtonLayoutProps } from '../button-layout/button-layout.types';
SegmentButton.propTypes = { SegmentButton.propTypes = {
children: string, children: string,

View File

@ -1,11 +1,10 @@
import { HTMLAttributes, ReactElement } from 'react'; import { HTMLAttributes, ReactElement } from 'react';
import { IconProps, IconWrapperProps } from '../../icon/icon.types'; import { IconWrapperProps } from '../../icon/icon.types';
export type SegmentedButton = IconWrapperProps & { export type SegmentedButton = IconWrapperProps & {
icon?: string; icon?: string;
toggled?: boolean; toggled?: boolean;
centralRipple?: boolean; centralRipple?: boolean;
children?: string | ReactElement<IconProps>;
}; };
export interface SegmentedButtons { export interface SegmentedButtons {

View File

@ -20,7 +20,4 @@ export type IconWrapperProps = IconPlacement &
icon?: string; icon?: string;
}; };
export type IconProps = SVGProps<SVGSVGElement> & export type IconProps = SVGProps<SVGSVGElement> & GeneralIconProps;
GeneralIconProps & {
children?: string | undefined;
};

View File

@ -3,8 +3,8 @@
import { bool } from 'prop-types'; import { bool } from 'prop-types';
import { CheckboxProps } from './checkbox.types'; import { CheckboxProps } from './checkbox.types';
import { RippleEffect } from '../../ripple/ripple-effect'; import { RippleEffect } from '../../ripple/ripple-effect';
import useRippleEffect from '../../ripple/hooks/useRippleEffect';
import { InputLayout } from '../input-layout/input-layout'; import { InputLayout } from '../input-layout/input-layout';
import useRippleEffect from '../../ripple/hooks/useRippleEffect';
import { import {
forwardRef, forwardRef,
useEffect, useEffect,
@ -51,7 +51,6 @@ export const Checkbox = forwardRef<HTMLInputElement, CheckboxProps>(
className={'m3-checkbox-ripple-layer'} className={'m3-checkbox-ripple-layer'}
ref={ripplesRef} ref={ripplesRef}
/> />
{props.children}
</div> </div>
); );
}, },

View File

@ -6,7 +6,3 @@ export interface InputLayoutProps
typeInput?: string; typeInput?: string;
type?: string; type?: string;
} }
export interface LabelPlacement {
labelPlacement?: 'left' | 'right';
}

View File

@ -1,18 +1,60 @@
import React, { forwardRef, HTMLAttributes } from 'react'; 'use client';
import { InputLayout } from '../input-layout/input-layout';
import { InputLayout } from '../input-layout/input-layout';
import React, {
ChangeEvent,
forwardRef,
InputHTMLAttributes,
useId,
useLayoutEffect,
useState,
} from 'react';
interface SliderProps extends InputHTMLAttributes<HTMLInputElement> {
options?: number[];
}
export const Slider = forwardRef<HTMLInputElement, SliderProps>(
({ options, ...props }, ref) => {
const sliderId = useId();
const [isChrome, setIsChrome] = useState<boolean>(false);
useLayoutEffect(() => {
setIsChrome(navigator.userAgent.indexOf('AppleWebKit') != -1);
}, []);
export const Slider = forwardRef<
HTMLInputElement,
HTMLAttributes<HTMLInputElement>
>((props, ref) => {
return ( return (
<div className={'m3 m3-slider-container'}> <div className={'m3 m3-slider-container'}>
<InputLayout <InputLayout
{...props}
className={props.className} className={props.className}
list={sliderId}
onChange={(event: ChangeEvent<HTMLInputElement>) => {
props.onChange?.apply(this, props?.onChange?.prototype);
if (isChrome) {
const fraction =
(parseInt(event.target.value) /
parseInt(event.target.max)) *
100;
event.target.style.background = `linear-gradient(\
to right, var(--md-sys-color-primary) ${fraction}%, \
var(--md-sys-color-surface-container-highest) ${fraction}%\
)`;
}
}}
ref={ref} ref={ref}
type={'range'} type={'range'}
typeInput={'slider'} typeInput={'slider'}
/> />
{options && (
<datalist id={sliderId}>
{options.map((option, index) => (
<option key={index} value={option} />
))}
</datalist>
)}
</div> </div>
); );
}); },
);

View File

@ -4,17 +4,15 @@ import { bool } from 'prop-types';
import React, { forwardRef } from 'react'; import React, { forwardRef } from 'react';
import { SwitchMainProps } from './switch.types'; import { SwitchMainProps } from './switch.types';
import { InputLayout } from '../input-layout/input-layout'; import { InputLayout } from '../input-layout/input-layout';
import { LabelPlacement } from '../input-layout/input-layout.types';
/** /**
* Switch component * Switch component
** description ** description
*/ */
export const Switch = forwardRef< export const Switch = forwardRef<HTMLInputElement, SwitchMainProps>(
HTMLInputElement, ({ icon, selected = false, ...props }, ref) => (
SwitchMainProps & LabelPlacement <>
>(({ icon, selected = false, ...props }, ref) => (
<div className={'m3 m3-switch'}> <div className={'m3 m3-switch'}>
<InputLayout <InputLayout
{...props} {...props}
@ -28,13 +26,19 @@ export const Switch = forwardRef<
<circle className={'m3 m3-switch-handler-state-layer'} /> <circle className={'m3 m3-switch-handler-state-layer'} />
<g> <g>
{icon && !selected && ( {icon && !selected && (
<text className={'m3 m3-icon-unchecked'}>close</text> <text className={'m3 m3-icon-unchecked'}>
close
</text>
)}
{icon && (
<text className={'m3 m3-icon-checked'}>check</text>
)} )}
{icon && <text className={'m3 m3-icon-checked'}>check</text>}
</g> </g>
</svg> </svg>
</div> </div>
)); </>
),
);
Switch.propTypes = { Switch.propTypes = {
icon: bool, icon: bool,

View File

@ -1,76 +1,25 @@
import { forwardRef, HTMLAttributes } from 'react'; import { createElement, forwardRef } from 'react';
import { getTypographyRole } from './utils/get-typography-role';
import {
TypographyProps,
Typography as TypographyTargetRef,
} from './typography.types';
export const Typography = { export const Typography = forwardRef<TypographyTargetRef, TypographyProps>(
h1: forwardRef<HTMLHeadingElement, HTMLAttributes<HTMLHeadingElement>>( ({ size, role, children, ...props }, ref) => {
(props, ref) => { const typeElement = getTypographyRole(role, size);
return (
<h1 const extraClasses =
className={`m3 m3-typography display-hero ${props.className}`} `m3 m3-typography ${size && role ? `${role}-${size}` : ''}`.trimEnd();
ref={ref}
> return createElement(
{props.children} typeElement,
</h1> {
...props,
ref: ref,
className: extraClasses,
},
children,
); );
}, },
),
h2: forwardRef<HTMLHeadingElement, HTMLAttributes<HTMLHeadingElement>>(
(props, ref) => {
return (
<h2
className={`m3 m3-typography display-large ${props.className}`}
ref={ref}
>
{props.children}
</h2>
); );
},
),
h3: forwardRef<HTMLHeadingElement, HTMLAttributes<HTMLHeadingElement>>(
(props, ref) => {
return (
<h3
className={`m3 m3-typography headline-medium ${props.className}`}
ref={ref}
>
{props.children}
</h3>
);
},
),
h4: forwardRef<HTMLHeadingElement, HTMLAttributes<HTMLHeadingElement>>(
(props, ref) => {
return (
<h4
className={`m3 m3-typography headline-small ${props.className}`}
ref={ref}
>
{props.children}
</h4>
);
},
),
h5: forwardRef<HTMLHeadingElement, HTMLAttributes<HTMLHeadingElement>>(
(props, ref) => {
return (
<h5
className={`m3 m3-typography title-medium ${props.className}`}
ref={ref}
>
{props.children}
</h5>
);
},
),
h6: forwardRef<HTMLHeadingElement, HTMLAttributes<HTMLHeadingElement>>(
(props, ref) => {
return (
<h6
className={`m3 m3-typography title-small ${props.className}`}
ref={ref}
>
{props.children}
</h6>
);
},
),
};

View File

@ -0,0 +1,21 @@
import { HTMLAttributes } from 'react';
export type Typography =
| HTMLLabelElement
| HTMLHeadingElement
| HTMLSpanElement
| HTMLParagraphElement;
export type TypographySize = 'hero' | 'xl' | 'small' | 'medium' | 'large';
export type TypographyRole =
| 'display'
| 'headline'
| 'title'
| 'body'
| 'label';
export interface TypographyProps extends HTMLAttributes<Typography> {
size: TypographySize;
role: TypographyRole;
}

View File

@ -0,0 +1,32 @@
import { TypographyRole, TypographySize } from '../typography.types';
export function getTypographyRole(role: TypographyRole, size: TypographySize) {
switch (role) {
case 'display':
return 'h1';
case 'headline':
switch (size) {
case 'large' || 'hero' || 'xl':
return 'h2';
case 'medium':
return 'h3';
case 'small':
return 'h4';
}
break;
case 'title':
switch (size) {
case 'large' || 'hero' || 'xl':
return 'h4';
case 'medium':
return 'h5';
case 'small':
return 'h6';
}
break;
case 'body':
return 'p';
case 'label':
return 'label';
}
}

View File

@ -30,6 +30,9 @@ div.m3.m3-segmented-buttons
& > svg > text & > svg > text
fill: var(--md-sys-color-on-surface) fill: var(--md-sys-color-on-surface)
& > span.m3.m3-ripple-domain > span.m3.ripple
background-color: color-mix(in srgb, var(--md-sys-color-on-secondary-container) 12%, transparent)
&:disabled &:disabled
border: 1px solid color-mix(in srgb, var(--md-sys-color-outline) 12%, transparent) border: 1px solid color-mix(in srgb, var(--md-sys-color-outline) 12%, transparent)
@ -61,6 +64,3 @@ div.m3.m3-segmented-buttons
& > span.m3.m3-button-segment-state-layer & > span.m3.m3-button-segment-state-layer
background-color: color-mix(in srgb, var(--md-sys-color-on-secondary-container) 12%, transparent) background-color: color-mix(in srgb, var(--md-sys-color-on-secondary-container) 12%, transparent)
&:active
& > span.m3.m3-ripple-domain > span.m3.ripple
background-color: color-mix(in srgb, var(--md-sys-color-on-secondary-container) 12%, transparent)

View File

@ -12,17 +12,17 @@ div.m3.m3-card
& > :is(div.m3-card-footer, header.m3-card-header, section.m3-card-body) > .m3-card-media:first-child, & > :is(div.m3-card-footer, header.m3-card-header, section.m3-card-body) > .m3-card-media:first-child,
& > .m3-card-media:first-child, & > .m3-card-media:first-child,
& > .m3-card-action-area:first-child > .m3-card-action-area-content > .m3-card-media:first-child & > .m3-card-action-area:first-child > .m3-card-action-area-content > .m3-card-media:first-child
padding: padding-calculating(false)
border-radius: 12px !important border-radius: 12px !important
padding: padding-calculating(false)
div.m3.m3-card > .m3-card-action-area:first-child > .m3-card-action-area-content > .m3-card-media:first-child div.m3.m3-card > .m3-card-action-area:first-child > .m3-card-action-area-content > .m3-card-media:first-child
padding: padding-calculating(false)
border-radius: 12px !important border-radius: 12px !important
padding: padding-calculating(false)
div.m3-card-footer, header.m3-card-header, section.m3-card-body, .m3-card-media.m3 div.m3-card-footer, header.m3-card-header, section.m3-card-body, .m3-card-media.m3
padding: padding-calculating(true)
display: block display: block
box-sizing: border-box box-sizing: border-box
padding: padding-calculating(true)
.m3.m3-card-media .m3.m3-card-media
&.m3-rounded &.m3-rounded
@ -40,7 +40,7 @@ div.m3.m3-card-action-area
contain: content contain: content
position: relative position: relative
border-radius: inherit border-radius: inherit
transition: box-shadow .2s cubic-bezier(0.2, 0, 0, 1) transition: background-color, box-shadow, .2s cubic-bezier(0.2, 0, 0, 1)
& > div.m3.m3-card-action-area-content & > div.m3.m3-card-action-area-content
top: 0 top: 0
@ -52,7 +52,9 @@ div.m3.m3-card-action-area
width: 100% width: 100%
height: 100% height: 100%
position: absolute position: absolute
transition: background-color .2s cubic-bezier(0.2, 0, 0, 1)
& > .m3.m3-ripple-domain > .m3.ripple
background-color: color-mix(in srgb, var(--md-sys-color-on-surface) 12%, transparent)
&:hover &:hover
& > span.m3.m3-card-state-layer & > span.m3.m3-card-state-layer
@ -79,6 +81,3 @@ div.m3.m3-card-action-area
&:not(&:has(span.m3.m3-ripple-domain)) &:not(&:has(span.m3.m3-ripple-domain))
& > span.m3.m3-card-state-layer & > span.m3.m3-card-state-layer
background-color: color-mix(in srgb, var(--md-sys-color-on-surface) 12%, transparent) background-color: color-mix(in srgb, var(--md-sys-color-on-surface) 12%, transparent)
& > span.m3.m3-ripple-domain > .m3.ripple
background-color: color-mix(in srgb, var(--md-sys-color-on-surface) 12%, transparent)

View File

@ -10,7 +10,7 @@ $weights: ("Thin": 100, "Light": 300, "Regular": 400, "Medium": 500, "Bold": 700
@each $name, $weight in $weights @each $name, $weight in $weights
@font-face @font-face
font-family: Roboto font-family: Roboto, sans-serif
font-face-name: #{$name} font-face-name: #{$name}
font-weight: #{$weight} font-weight: #{$weight}
src: url("./font/Roboto-#{$name}.ttf") src: url("./font/Roboto-#{$name}.ttf")

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

View File

@ -5,6 +5,7 @@
@import "ripple" @import "ripple"
@import "divider" @import "divider"
@import "container" @import "container"
@import "typography"
@import "mixins/m3-mixins" @import "mixins/m3-mixins"
@ -19,9 +20,9 @@
@import "input-styles/checkbox" @import "input-styles/checkbox"
@import "input-styles/text-field" @import "input-styles/text-field"
@import "./themes/tokens.css" @import "./themes/tokens"
@import "./themes/colors.module.css" @import "./themes/colors.module"
@import "./themes/typography.module.css" @import "./themes/typography.module"
@import "./themes/theme.dark.css" (prefers-color-scheme: dark) @import "./themes/theme.dark.css" (prefers-color-scheme: dark)
@import "./themes/theme.light.css" (prefers-color-scheme: light) @import "./themes/theme.light.css" (prefers-color-scheme: light)
@ -30,9 +31,6 @@ html
color: var(--md-sys-color-on-surface) color: var(--md-sys-color-on-surface)
background-color: var(--md-sys-color-surface-container) background-color: var(--md-sys-color-surface-container)
.m3
user-select: none
.m3.m3-wrapper .m3.m3-wrapper
position: relative position: relative
display: block display: block

View File

@ -3,15 +3,16 @@ svg.m3.m3-svg-icon
$types: ["Outlined", "Rounded", "Sharp"] $types: ["Outlined", "Rounded", "Sharp"]
& > text & > text
alignment-baseline: central
text-anchor: middle text-anchor: middle
dominant-baseline: central
alignment-baseline: central
@each $size in $sizes @each $size in $sizes
& > text.m3-size-#{$size} & > text.m3-size-#{$size}
width: $size width: $size
aspect-ratio: 1 aspect-ratio: 1
line-height: $size
font-size: $size font-size: $size
line-height: $size
@each $type in $types @each $type in $types
& > text.m3-#{$type} & > text.m3-#{$type}

View File

@ -1,11 +1,10 @@
div.m3.m3-checkbox-container div.m3.m3-checkbox-container
@include m3-label-mixin @include m3-checkbox-container-mixin
width: 18px
height: 18px
& > span.m3.m3-checkbox-state-layer & > span.m3.m3-checkbox-state-layer
@include m3-state-layer-mixin @include m3-state-layer-mixin
span.m3.m3-checkbox-ripple-layer & > span.m3.m3-checkbox-ripple-layer
z-index: 20 z-index: 20
width: 2.5rem width: 2.5rem
height: 2.5rem height: 2.5rem
@ -14,7 +13,6 @@ span.m3.m3-checkbox-ripple-layer
position: absolute position: absolute
input[type="checkbox"].m3.m3-checkbox input[type="checkbox"].m3.m3-checkbox
margin: 0
z-index: 10 z-index: 10
display: flex display: flex
width: 1.125rem width: 1.125rem
@ -28,6 +26,7 @@ input[type="checkbox"].m3.m3-checkbox
transition: background-color .2s cubic-bezier(0.2, 0, 0, 1) transition: background-color .2s cubic-bezier(0.2, 0, 0, 1)
& ~ span.m3-checkbox-state & ~ span.m3-checkbox-state
position: absolute
color: var(--md-sys-color-on-surface-variant) color: var(--md-sys-color-on-surface-variant)
transition: color .2s cubic-bezier(0.2, 0, 0, 1) transition: color .2s cubic-bezier(0.2, 0, 0, 1)
@ -55,13 +54,13 @@ input[type="checkbox"].m3.m3-checkbox
opacity: 38% opacity: 38%
& ~ span.m3-checkbox-state & ~ span.m3-checkbox-state
pointer-events: none
z-index: 10 z-index: 10
display: flex display: flex
font-size: 24px font-size: 24px
font-weight: 700 font-weight: 700
line-height: 24px line-height: 24px
align-items: center align-items: center
pointer-events: none
justify-content: center justify-content: center
font-family: Material-Symbols-Outlined-Regular, sans-serif font-family: Material-Symbols-Outlined-Regular, sans-serif
font-variation-settings: 'FILL' 1, 'wght' 300, 'GRAD' 0, 'opsz' 24 font-variation-settings: 'FILL' 1, 'wght' 300, 'GRAD' 0, 'opsz' 24
@ -83,6 +82,7 @@ input[type="checkbox"].m3.m3-checkbox
&:is(:user-invalid:is(:active, :indeterminate:active), .m3.m3-error:active) ~ span.m3.m3-checkbox-state-layer &:is(:user-invalid:is(:active, :indeterminate:active), .m3.m3-error:active) ~ span.m3.m3-checkbox-state-layer
background-color: color-mix(in srgb, var(--md-sys-color-error) 12%, transparent) background-color: color-mix(in srgb, var(--md-sys-color-error) 12%, transparent)
& ~ span.m3-ripple-domain > .m3.ripple & ~ span.m3-ripple-domain > .m3.ripple
background-color: color-mix(in srgb, var(--md-sys-color-error) 20%, transparent) background-color: color-mix(in srgb, var(--md-sys-color-error) 20%, transparent)
@ -91,6 +91,7 @@ input[type="checkbox"].m3.m3-checkbox
&:is(:checked:active, :indeterminate:active) ~ span.m3.m3-checkbox-state-layer &:is(:checked:active, :indeterminate:active) ~ span.m3.m3-checkbox-state-layer
background-color: color-mix(in srgb, var(--md-sys-color-primary) 12%, transparent) background-color: color-mix(in srgb, var(--md-sys-color-primary) 12%, transparent)
& ~ span.m3-ripple-domain > .m3.ripple & ~ span.m3-ripple-domain > .m3.ripple
background-color: color-mix(in srgb, var(--md-sys-color-on-surface) 20%, transparent) background-color: color-mix(in srgb, var(--md-sys-color-on-surface) 20%, transparent)
@ -100,6 +101,7 @@ input[type="checkbox"].m3.m3-checkbox
&:active ~ span.m3.m3-checkbox-state-layer &:active ~ span.m3.m3-checkbox-state-layer
background-color: color-mix(in srgb, var(--md-sys-color-on-surface) 12%, transparent) background-color: color-mix(in srgb, var(--md-sys-color-on-surface) 12%, transparent)
& ~ span.m3-ripple-domain > .m3.ripple & ~ span.m3-ripple-domain > .m3.ripple
background-color: color-mix(in srgb, var(--md-sys-color-primary) 20%, transparent) background-color: color-mix(in srgb, var(--md-sys-color-primary) 20%, transparent)

View File

@ -5,12 +5,16 @@ div.m3.m3-radio-container
display: inline-flex display: inline-flex
justify-content: center justify-content: center
& + label.m3.m3-radio-label
margin-inline: 3px
& > span.m3-checkbox-ripple-layer, span.m3.m3-radio-state-layer & > span.m3-checkbox-ripple-layer, span.m3.m3-radio-state-layer
z-index: 5 z-index: 5
width: 40px
height: 40px
aspect-ratio: 1
& > span.m3.m3-radio-state-layer & > span.m3.m3-radio-state-layer
width: 40px
aspect-ratio: 1
border-radius: 50% border-radius: 50%
position: absolute position: absolute
pointer-events: none pointer-events: none

View File

@ -3,41 +3,51 @@ div.m3.m3-slider-container
display: flex display: flex
align-items: center align-items: center
& > datalist
display: none
input[type="range"].m3.m3-slider input[type="range"].m3.m3-slider
margin: 0 margin: 0
height: 4px
appearance: none appearance: none
overflow: visible
border-radius: 2px border-radius: 2px
margin-inline: 10px margin-inline: 8px
background-color: var(--md-sys-color-surface-container-highest) background: var(--md-sys-color-surface-container-highest)
&:-moz-any(&)
height: 4px
&:-webkit-any(&)
background: linear-gradient(to right, var(--md-sys-color-primary) 0%, var(--md-sys-color-surface-container-highest) 0%)
&::-moz-range-track
height: 4px
overflow: hidden
border-radius: 2px
&::-moz-range-progress
height: 4px
overflow: hidden
border-radius: 2px
background-color: var(--md-sys-color-primary)
&::-moz-range-thumb
@include input-range-thumb-mixin
border: none
&::-webkit-slider-container &::-webkit-slider-container
margin-inline: -8px appearance: none
box-shadow: none
border-radius: 2px
min-block-size: 4px
height: 4px
&::-webkit-slider-runnable-track
height: 20px
&::-webkit-slider-thumb &::-webkit-slider-thumb
@include elevation-1(false) @include input-range-thumb-mixin
&::after
width: 40px
aspect-ratio: 1
background-color: transparent
&:hover
outline: 10px solid color-mix(in srgb, var(--md-sys-color-primary) 8%, transparent)
&:is(:active, :focus-visible)
outline: 10px solid color-mix(in srgb, var(--md-sys-color-primary) 12%, transparent)
width: 20px
aspect-ratio: 1
appearance: none
overflow: visible
border-radius: 50%
box-sizing: border-box
outline: 10px solid transparent
background: var(--md-sys-color-primary)
transition: .2s cubic-bezier(0.2, 0, 0, 1)

View File

@ -1,30 +1,29 @@
div.m3.m3-switch div.m3.m3-switch
margin: 4px
gap: 20px gap: 20px
box-sizing: content-box margin: 4px
height: 32px
display: flex display: flex
align-items: center align-items: center
justify-content: center justify-content: left
width: 52px box-sizing: content-box
height: 32px
& > svg & > svg
overflow: visible
transition: .2s cubic-bezier(0.175, 0.885, 0.32, 1.275)
width: 52px width: 52px
height: 32px height: 32px
overflow: visible
transition: .2s cubic-bezier(0.175, 0.885, 0.32, 1.275)
& > g & > g
transform: translate(11.5%, 81%) transform: translate(11.5%, 81%)
transition: .2s cubic-bezier(0.175, 0.885, 0.32, 1.275) transition: .2s cubic-bezier(0.175, 0.885, 0.32, 1.275)
& > text & > text
font-family: Material-Symbols-Outlined-Regular
font-size: 20px font-size: 20px
font-family: Material-Symbols-Outlined-Regular,serif
& > circle.m3.m3-switch-handler-state-layer, & > circle.m3.m3-switch-handler & > circle.m3.m3-switch-handler-state-layer, & > circle.m3.m3-switch-handler
transition: .2s cubic-bezier(0.175, 0.885, 0.32, 1.275)
cy: 50% cy: 50%
cx: 16px cx: 16px
transition: .2s cubic-bezier(0.175, 0.885, 0.32, 1.275)
& > circle.m3.m3-switch-handler-state-layer & > circle.m3.m3-switch-handler-state-layer
r: 20px r: 20px
@ -42,13 +41,13 @@ div.m3.m3-switch
height: 30px height: 30px
& > input.m3 & > input.m3
cursor: pointer
appearance: none
opacity: 0 !important
margin: 0 margin: 0
width: 52px width: 52px
height: 32px height: 32px
cursor: pointer
appearance: none
position: absolute position: absolute
opacity: 0 !important
&:disabled &:disabled
cursor: not-allowed cursor: not-allowed
@ -93,8 +92,8 @@ div.m3.m3-switch
r: 14px r: 14px
&:not(:checked):disabled + svg > circle.m3.m3-switch-handler &:not(:checked):disabled + svg > circle.m3.m3-switch-handler
fill: var(--md-sys-color-on-surface)
fill-opacity: 38% fill-opacity: 38%
fill: var(--md-sys-color-on-surface)
&:hover:not(:disabled) &:hover:not(:disabled)
&:checked + svg &:checked + svg
@ -102,25 +101,25 @@ div.m3.m3-switch
fill: var(--md-sys-color-primary-container) fill: var(--md-sys-color-primary-container)
& > circle.m3.m3-switch-handler-state-layer & > circle.m3.m3-switch-handler-state-layer
fill: var(--md-sys-color-primary)
fill-opacity: 8% fill-opacity: 8%
fill: var(--md-sys-color-primary)
&:not(:checked) + svg &:not(:checked) + svg
& > circle.m3.m3-switch-handler & > circle.m3.m3-switch-handler
fill: var(--md-sys-color-on-surface-variant) fill: var(--md-sys-color-on-surface-variant)
& > circle.m3.m3-switch-handler-state-layer & > circle.m3.m3-switch-handler-state-layer
fill: var(--md-sys-color-on-surface)
fill-opacity: 8% fill-opacity: 8%
fill: var(--md-sys-color-on-surface)
&:active:not(:disabled) &:active:not(:disabled)
&:checked + svg > circle.m3.m3-switch-handler-state-layer &:checked + svg > circle.m3.m3-switch-handler-state-layer
fill: var(--md-sys-color-primary)
fill-opacity: 12% fill-opacity: 12%
fill: var(--md-sys-color-primary)
&:not(:checked) + svg > circle.m3.m3-switch-handler-state-layer &:not(:checked) + svg > circle.m3.m3-switch-handler-state-layer
fill: var(--md-sys-color-on-surface)
fill-opacity: 12% fill-opacity: 12%
fill: var(--md-sys-color-on-surface)
&:is(:checked, :checked:disabled) + svg > rect.m3.m3-switch-track &:is(:checked, :checked:disabled) + svg > rect.m3.m3-switch-track
rx: 16px rx: 16px
@ -137,8 +136,8 @@ div.m3.m3-switch
fill: var(--md-sys-color-surface-container-highest) fill: var(--md-sys-color-surface-container-highest)
&:checked + svg > rect.m3.m3-switch-track &:checked + svg > rect.m3.m3-switch-track
stroke: var(--md-sys-color-primary)
fill: var(--md-sys-color-primary) fill: var(--md-sys-color-primary)
stroke: var(--md-sys-color-primary)
&:disabled + svg &:disabled + svg
& > g > text.m3 & > g > text.m3
@ -154,5 +153,5 @@ div.m3.m3-switch
fill: color-mix(in srgb, var(--md-sys-color-on-surface) 38%, transparent) fill: color-mix(in srgb, var(--md-sys-color-on-surface) 38%, transparent)
& > rect.m3.m3-switch-track & > rect.m3.m3-switch-track
stroke: color-mix(in srgb, var(--md-sys-color-on-surface) 0%, transparent)
fill: color-mix(in srgb, var(--md-sys-color-on-surface) 12%, transparent) fill: color-mix(in srgb, var(--md-sys-color-on-surface) 12%, transparent)
stroke: color-mix(in srgb, var(--md-sys-color-on-surface) 0%, transparent)

View File

@ -1,15 +1,17 @@
@mixin m3-label-mixin @mixin m3-checkbox-container-mixin
gap: 16px
height: 40px
display: flex display: flex
aspect-ratio: 1
position: relative position: relative
align-items: center align-items: center
justify-content: center justify-content: center
aspect-ratio: 1
@mixin m3-state-layer-mixin @mixin m3-state-layer-mixin
position: absolute width: 40px
width: 2.5rem aspect-ratio: 1
aspect-ratio: inherit
border-radius: 50% border-radius: 50%
position: absolute
transition: background-color .2s cubic-bezier(0.2, 0, 0, 1) transition: background-color .2s cubic-bezier(0.2, 0, 0, 1)
@mixin m3-fab-default($b-radius, $width, $height : $width, $padding : 0) @mixin m3-fab-default($b-radius, $width, $height : $width, $padding : 0)
@ -73,3 +75,25 @@
right: 0 right: 0
position: absolute position: absolute
background: #00000000 background: #00000000
@mixin input-range-thumb-mixin
@include elevation-1(false)
&:hover
&:after
content: '1'
outline: 10px solid color-mix(in srgb, var(--md-sys-color-primary) 8%, transparent)
&:is(:active, :focus-visible)
outline: 10px solid color-mix(in srgb, var(--md-sys-color-primary) 12%, transparent)
width: 20px
height: 20px
appearance: none
border-radius: 50%
box-sizing: border-box
outline: 10px solid transparent
background: var(--md-sys-color-primary)
transition: .2s cubic-bezier(0.2, 0, 0, 1)

View File

@ -14,10 +14,15 @@
z-index: 20 z-index: 20
aspect-ratio: 1 aspect-ratio: 1
border-radius: 50% border-radius: 50%
animation-duration: .55s
animation-iteration-count: 1
animation-name: rippleAppearanceAnimation animation-name: rippleAppearanceAnimation
animation-timing-function: cubic-bezier(0.4, 0, 0.2, 1) animation-timing-function: cubic-bezier(0.4, 0, 0.2, 1)
animation-duration: .55s
transition: opacity, background, background-color, .55s cubic-bezier(0.4, 0, 0.2, 1) transition-duration: .55s
transition-property: opacity, background-color
transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1)
&.visible &.visible
opacity: 1 !important opacity: 1 !important

View File

@ -0,0 +1,5 @@
:is(h1, h2, h3, h4, h5, h6, code, p, span, pre, label).m3.m3-typography {
vertical-align: center;
}
/*# sourceMappingURL=typography.css.map */

View File

@ -0,0 +1 @@
{"version":3,"sourceRoot":"","sources":["typography.sass"],"names":[],"mappings":"AAAA;EACI","file":"typography.css"}

View File

@ -0,0 +1,4 @@
label
white-space: nowrap
display: inline-block
vertical-align: middle