Add the Material 3 Expressive foundation
tests / lint (push) Successful in 1m0s
tests / feature (8.4) (push) Successful in 1m0s
tests / feature (8.5) (push) Successful in 1m1s
tests / browser (safari, webkit) (push) Successful in 1m59s
tests / browser (chrome, chromium) (push) Successful in 1m49s
tests / browser (firefox, firefox) (push) Successful in 1m54s

Colour, shape, type, elevation and motion as tokens and Tailwind
utilities; `php artisan material:scheme`, which generates an app's colour
roles with Google's material-color-utilities (spec 2025); the theme head
script with light, dark and system and its Alpine store; Google Sans Flex;
every Material Symbol (4,135, outlined and filled) drawn by <x-icon>
without blade-icons; all 35 M3 Expressive shapes, ported from androidx, as
<x-shape>; the x-figure directive; the Toasts concern; DesignGuard for
applications' tests; and a showcase with every token, both themes side by
side and an icon search.

Colour utilities are `@theme inline`, so a section with its own
data-theme repaints; without it they resolve once on :root.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01V9NnLxnPp8vaaurb3Z1MFy
This commit is contained in:
Andreas Reinhold / reini
2026-09-13 05:24:11 +02:00
co-authored by Claude Opus 5
parent 9b53891a8e
commit b48e879254
8364 changed files with 12720 additions and 28 deletions
+6
View File
@@ -54,6 +54,12 @@ jobs:
tools: composer:v2 tools: composer:v2
coverage: none coverage: none
# material:scheme's tests run the bundled colour script through Node.
- name: Setup Node
uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6
with:
node-version: '22'
- name: Install dependencies - name: Install dependencies
run: composer install --no-interaction --no-progress --prefer-dist run: composer install --no-interaction --no-progress --prefer-dist
+30
View File
@@ -0,0 +1,30 @@
Livewire Material
Copyright (c) 2026 noNameWEB — MIT License (LICENSE)
This package bundles or derives from the following works.
Material Symbols (resources/svg/symbols)
From google/material-design-icons, commit recorded in resources/svg/symbols/SOURCE.
Copyright Google LLC. Apache License 2.0.
M3 Expressive shapes (resources/svg/shapes)
Geometry ported from androidx Compose Material 3 (MaterialShapes.kt) and androidx
graphics-shapes, as recorded at the top of bin/shapes.mjs.
Copyright The Android Open Source Project. Apache License 2.0.
Google Sans Flex (resources/fonts/google-sans-flex)
Copyright Google LLC. SIL Open Font License 1.1 (resources/fonts/google-sans-flex/OFL.txt).
Subset: Latin and Latin Extended, weight 400700, roundness 0100.
material-color-utilities (resources/node/scheme.mjs)
Bundled from @material/material-color-utilities.
Copyright Google LLC. Apache License 2.0; its license header is kept in the bundle.
Design token values (resources/css/tokens: shape, elevation, type, motion easings)
From @material/web's token files. Copyright Google LLC. Apache License 2.0.
Spring motion constants (resources/css/tokens/motion.css)
From androidx Compose Material 3, tokens/ExpressiveMotionTokens.kt.
Copyright The Android Open Source Project. Apache License 2.0.
A copy of the Apache License 2.0 is available at https://www.apache.org/licenses/LICENSE-2.0.
+65
View File
@@ -0,0 +1,65 @@
#!/usr/bin/env bash
#
# Refresh resources/svg/symbols from google/material-design-icons (Apache-2.0).
#
# Material Symbols Rounded at weight 400, grade 0 and optical size 24, outlined and
# filled, for every symbol Google publishes. Maintenance only: applications never run
# this, and bin/ is not part of the archive Composer installs.
#
# The repository is ~5 GB, so it is cloned without blobs and checked out sparsely:
# git downloads only the two files per symbol that are kept. The @material-symbols
# npm packages were not used because their SVGs are drawn at optical size 48, whose
# strokes are thinner than the 24px design.
set -euo pipefail
root="$(cd "$(dirname "$0")/.." && pwd)"
work="$(mktemp -d)"
trap 'rm -rf "$work"' EXIT
git clone --quiet --depth=1 --filter=blob:none --no-checkout \
https://github.com/google/material-design-icons.git "$work/repo"
cd "$work/repo"
git sparse-checkout set --no-cone \
'/symbols/web/*/materialsymbolsrounded/*_24px.svg' \
'!/symbols/web/*/materialsymbolsrounded/*_wght[0-9]*' \
'!/symbols/web/*/materialsymbolsrounded/*_grad200*' \
'!/symbols/web/*/materialsymbolsrounded/*_gradN25*'
git checkout --quiet "$(git symbolic-ref --short HEAD)"
out="$root/resources/svg/symbols"
rm -rf "$out/outlined" "$out/filled"
mkdir -p "$out/outlined" "$out/filled"
# Google's files carry width="24" height="24" and no fill. Drop the size so CSS decides
# it, and paint in currentColor so an icon takes the colour of the text around it.
normalise() {
sed -E \
-e 's/ (width|height)="[0-9]+"//g' \
-e 's/<svg /<svg fill="currentColor" /' \
"$1"
}
missing=0
for dir in symbols/web/*/materialsymbolsrounded; do
name="$(basename "$(dirname "$dir")")"
outlined="$dir/${name}_24px.svg"
filled="$dir/${name}_fill1_24px.svg"
if [[ ! -f "$outlined" || ! -f "$filled" ]]; then
echo "skipped $name: no outlined and filled 24px pair" >&2
missing=$((missing + 1))
continue
fi
normalise "$outlined" > "$out/outlined/$name.svg"
normalise "$filled" > "$out/filled/$name.svg"
done
git rev-parse HEAD > "$out/SOURCE"
echo "$(ls "$out/outlined" | wc -l | tr -d ' ') symbols from google/material-design-icons@$(cut -c1-12 "$out/SOURCE"), $missing skipped"
+118
View File
@@ -0,0 +1,118 @@
/**
* The source of resources/node/scheme.mjs, which `php artisan material:scheme` runs.
*
* Rebuild with `npm run build:scheme` after changing this file or upgrading
* @material/material-color-utilities; the bundle is committed so applications need
* nothing but `node`. (The published library imports without file extensions, which
* plain Node refuses, so it cannot be run unbundled anyway.)
*
* Input: one JSON argument — {seed, variant, contrast, success, warning, info}.
* Output: JSON on stdout — {seed, variant, spec, contrast, light: {role: hex}, dark: {role: hex}}.
*/
import {
argbFromHex,
customColor,
hexFromArgb,
Hct,
MaterialDynamicColors,
SchemeContent,
SchemeExpressive,
SchemeFidelity,
SchemeFruitSalad,
SchemeMonochrome,
SchemeNeutral,
SchemeRainbow,
SchemeTonalSpot,
SchemeVibrant,
} from '@material/material-color-utilities'
const VARIANTS = {
'tonal-spot': SchemeTonalSpot,
vibrant: SchemeVibrant,
expressive: SchemeExpressive,
neutral: SchemeNeutral,
fidelity: SchemeFidelity,
content: SchemeContent,
monochrome: SchemeMonochrome,
rainbow: SchemeRainbow,
'fruit-salad': SchemeFruitSalad,
}
// Key colours seed the palettes; they are not roles a stylesheet paints with.
const NOT_ROLES = /_palette_key_color$/
function fail(message) {
process.stderr.write(`${message}\n`)
process.exit(1)
}
let input
try {
input = JSON.parse(process.argv[2] ?? '')
} catch {
fail('Expected one JSON argument: {"seed": "#rrggbb", "variant": "tonal-spot", ...}')
}
const hex = /^#[0-9a-f]{6}$/i
const Scheme = VARIANTS[input.variant]
if (!hex.test(input.seed ?? '')) fail(`The seed must be a #rrggbb colour, "${input.seed}" given.`)
if (!Scheme) fail(`Unknown variant "${input.variant}". Use one of: ${Object.keys(VARIANTS).join(', ')}.`)
for (const state of ['success', 'warning', 'info']) {
if (!hex.test(input[state] ?? '')) fail(`The ${state} colour must be a #rrggbb colour, "${input[state]}" given.`)
}
const contrast = Number(input.contrast ?? 0)
if (!(contrast >= -1 && contrast <= 1)) fail(`The contrast level must be between -1 and 1, "${input.contrast}" given.`)
const source = Hct.fromInt(argbFromHex(input.seed))
const colors = new MaterialDynamicColors()
function roles(isDark) {
// The 2025 spec is M3 Expressive's colour; the library falls back to 2021 for the
// variants the new spec does not define (fidelity, content, monochrome, …).
const scheme = new Scheme(source, isDark, contrast, '2025')
const out = {}
for (const color of colors.allColors) {
if (color && !NOT_ROLES.test(color.name)) {
out[color.name.replaceAll('_', '-')] = hexFromArgb(color.getArgb(scheme))
}
}
return { scheme, out }
}
const light = roles(false)
const dark = roles(true)
// success, warning and info are M3 custom colours, harmonisation off: harmonising pulls
// each hue towards the seed, and a state has to stay recognisable whatever the brand is.
for (const state of ['success', 'warning', 'info']) {
const group = customColor(argbFromHex(input.seed), { name: state, value: argbFromHex(input[state]), blend: false })
for (const [theme, colours] of [['light', light.out], ['dark', dark.out]]) {
colours[state] = hexFromArgb(group[theme].color)
colours[`on-${state}`] = hexFromArgb(group[theme].onColor)
colours[`${state}-container`] = hexFromArgb(group[theme].colorContainer)
colours[`on-${state}-container`] = hexFromArgb(group[theme].onColorContainer)
}
}
// A state colour drawn on the inverse surface (a snackbar's icon) is the other theme's.
for (const state of ['error', 'success', 'warning', 'info']) {
light.out[`inverse-${state}`] = dark.out[state]
dark.out[`inverse-${state}`] = light.out[state]
}
process.stdout.write(JSON.stringify({
seed: input.seed.toLowerCase(),
variant: input.variant,
spec: light.scheme.specVersion,
contrast,
light: light.out,
dark: dark.out,
}))
+881
View File
@@ -0,0 +1,881 @@
/**
* Regenerates resources/svg/shapes: one SVG per Material 3 Expressive shape.
*
* Run from the repository root with `npm run build:shapes` (or `node bin/shapes.mjs`).
* Maintenance only: plain Node 22+, no dependencies, and the output is deterministic,
* so running it twice changes nothing.
*
* The geometry is not traced but ported from androidx, which defines every shape as a
* RoundedPolygon — vertices plus per-vertex CornerRounding — and turns it into cubic
* Béziers. That construction is reproduced here line for line (with doubles in place of
* Kotlin's Floats), then each shape is normalised as MaterialShapes does, scaled so its
* exact bounds span 96 units on the larger axis, and centred in a 100 × 100 viewBox.
* Where a control point would still land outside the viewBox, that cubic is split into
* pieces of the same curve (see `contained`), so every coordinate in a file lies in 0100.
*
* ---------------------------------------------------------------------------------------
* Ported from androidx (https://github.com/androidx/androidx), commit
* 27cf9a7d5788aa0f5f2d8b6699ce279560daf326:
*
* compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/MaterialShapes.kt
* compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/internal/ShapeUtil.kt
* compose/ui/ui-graphics/src/commonMain/kotlin/androidx/compose/ui/graphics/Matrix.kt (rotateZ, scale)
* graphics/graphics-shapes/src/commonMain/kotlin/androidx/graphics/shapes/CornerRounding.kt
* graphics/graphics-shapes/src/commonMain/kotlin/androidx/graphics/shapes/Cubic.kt
* graphics/graphics-shapes/src/commonMain/kotlin/androidx/graphics/shapes/Point.kt
* graphics/graphics-shapes/src/commonMain/kotlin/androidx/graphics/shapes/RoundedPolygon.kt
* graphics/graphics-shapes/src/commonMain/kotlin/androidx/graphics/shapes/Shapes.kt
* graphics/graphics-shapes/src/commonMain/kotlin/androidx/graphics/shapes/Utils.kt
*
* Copyright 2022-2024 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* ---------------------------------------------------------------------------------------
*/
import { mkdirSync, readdirSync, rmSync, writeFileSync } from 'node:fs'
import { join } from 'node:path'
const OUTPUT = 'resources/svg/shapes'
const VIEWBOX = 100
const FILL = 96
const DISTANCE_EPSILON = 1e-4
// Point.kt / Utils.kt ------------------------------------------------------------------
const point = (x, y) => ({ x, y })
const plus = (a, b) => point(a.x + b.x, a.y + b.y)
const minus = (a, b) => point(a.x - b.x, a.y - b.y)
const times = (a, k) => point(a.x * k, a.y * k)
const div = (a, k) => point(a.x / k, a.y / k)
const dot = (a, b) => a.x * b.x + a.y * b.y
const length = (a) => Math.sqrt(a.x * a.x + a.y * a.y)
const rotate90 = (a) => point(-a.y, a.x)
const clockwise = (a, b) => a.x * b.y - a.y * b.x > 0
const lerp = (a, b, f) => (1 - f) * a + f * b
const lerpPoint = (a, b, f) => point(lerp(a.x, b.x, f), lerp(a.y, b.y, f))
function direction(a) {
const d = length(a)
if (!(d > 0)) {
throw new Error("Can't get the direction of a 0-length vector")
}
return div(a, d)
}
const radialToCartesian = (radius, angle) => point(Math.cos(angle) * radius, Math.sin(angle) * radius)
const convex = (previous, current, next) => clockwise(minus(current, previous), minus(next, current))
// Cubic.kt ------------------------------------------------------------------------------
/** A cubic is [anchor0X, anchor0Y, control0X, control0Y, control1X, control1Y, anchor1X, anchor1Y]. */
const cubic = (a0, c0, c1, a1) => [a0.x, a0.y, c0.x, c0.y, c1.x, c1.y, a1.x, a1.y]
function straightLine(x0, y0, x1, y1) {
return [x0, y0, lerp(x0, x1, 1 / 3), lerp(y0, y1, 1 / 3), lerp(x0, x1, 2 / 3), lerp(y0, y1, 2 / 3), x1, y1]
}
function circularArc(centerX, centerY, x0, y0, x1, y1) {
const p0d = direction(point(x0 - centerX, y0 - centerY))
const p1d = direction(point(x1 - centerX, y1 - centerY))
const rotatedP0 = rotate90(p0d)
const rotatedP1 = rotate90(p1d)
const isClockwise = dot(rotatedP0, point(x1 - centerX, y1 - centerY)) >= 0
const cosa = dot(p0d, p1d)
if (cosa > 0.999) {
return straightLine(x0, y0, x1, y1)
}
const k =
(((length(point(x0 - centerX, y0 - centerY)) * 4) / 3) *
(Math.sqrt(2 * (1 - cosa)) - Math.sqrt(1 - cosa * cosa))) /
(1 - cosa) *
(isClockwise ? 1 : -1)
return [x0, y0, x0 + rotatedP0.x * k, y0 + rotatedP0.y * k, x1 - rotatedP1.x * k, y1 - rotatedP1.y * k, x1, y1]
}
function pointOnCurve(c, t) {
const u = 1 - t
return point(
c[0] * (u * u * u) + c[2] * (3 * t * u * u) + c[4] * (3 * t * t * u) + c[6] * (t * t * t),
c[1] * (u * u * u) + c[3] * (3 * t * u * u) + c[5] * (3 * t * t * u) + c[7] * (t * t * t),
)
}
function split(c, t) {
const u = 1 - t
const p = pointOnCurve(c, t)
return [
[
c[0],
c[1],
c[0] * u + c[2] * t,
c[1] * u + c[3] * t,
c[0] * (u * u) + c[2] * (2 * u * t) + c[4] * (t * t),
c[1] * (u * u) + c[3] * (2 * u * t) + c[5] * (t * t),
p.x,
p.y,
],
[
p.x,
p.y,
c[2] * (u * u) + c[4] * (2 * u * t) + c[6] * (t * t),
c[3] * (u * u) + c[5] * (2 * u * t) + c[7] * (t * t),
c[4] * u + c[6] * t,
c[5] * u + c[7] * t,
c[6],
c[7],
],
]
}
const reverse = (c) => [c[6], c[7], c[4], c[5], c[2], c[3], c[0], c[1]]
const zeroLength = (c) => Math.abs(c[0] - c[6]) < DISTANCE_EPSILON && Math.abs(c[1] - c[7]) < DISTANCE_EPSILON
/** The parameters in (0, 1) where one axis of a cubic turns: the roots of its derivative. */
function turningPoints(c, axis) {
const [p0, p1, p2, p3] = [c[axis], c[axis + 2], c[axis + 4], c[axis + 6]]
const a = -p0 + 3 * p1 - 3 * p2 + p3
const b = 2 * (p0 - 2 * p1 + p2)
const k = p1 - p0
const roots = []
if (Math.abs(a) < 1e-9) {
if (Math.abs(b) > 1e-9) {
roots.push(-k / b)
}
} else if (b * b - 4 * a * k >= 0) {
const root = Math.sqrt(b * b - 4 * a * k)
roots.push((-b + root) / (2 * a), (-b - root) / (2 * a))
}
return roots.filter((t) => t > 1e-6 && t < 1 - 1e-6)
}
/** Axis-aligned bounds of one cubic: of all four points when approximate, else of the curve itself. */
function cubicBounds(c, approximate) {
const xs = [c[0], c[6]]
const ys = [c[1], c[7]]
if (approximate) {
xs.push(c[2], c[4])
ys.push(c[3], c[5])
} else {
turningPoints(c, 0).forEach((t) => xs.push(pointOnCurve(c, t).x))
turningPoints(c, 1).forEach((t) => ys.push(pointOnCurve(c, t).y))
}
return [Math.min(...xs), Math.min(...ys), Math.max(...xs), Math.max(...ys)]
}
// CornerRounding.kt ---------------------------------------------------------------------
const rounding = (radius = 0, smoothing = 0) => ({ radius, smoothing })
const UNROUNDED = rounding()
// RoundedPolygon.kt ---------------------------------------------------------------------
class RoundedCorner {
constructor(p0, p1, p2, cornerRounding) {
this.p0 = p0
this.p1 = p1
this.p2 = p2
const v01 = minus(p0, p1)
const v21 = minus(p2, p1)
const d01 = length(v01)
const d21 = length(v21)
if (d01 > 0 && d21 > 0) {
this.d1 = div(v01, d01)
this.d2 = div(v21, d21)
this.cornerRadius = cornerRounding?.radius ?? 0
this.smoothing = cornerRounding?.smoothing ?? 0
this.cosAngle = dot(this.d1, this.d2)
this.sinAngle = Math.sqrt(1 - this.cosAngle * this.cosAngle)
this.expectedRoundCut = this.sinAngle > 1e-3 ? (this.cornerRadius * (this.cosAngle + 1)) / this.sinAngle : 0
} else {
this.d1 = point(0, 0)
this.d2 = point(0, 0)
this.cornerRadius = 0
this.smoothing = 0
this.cosAngle = 0
this.sinAngle = 0
this.expectedRoundCut = 0
}
}
get expectedCut() {
return (1 + this.smoothing) * this.expectedRoundCut
}
getCubics(allowedCut0, allowedCut1 = allowedCut0) {
const allowedCut = Math.min(allowedCut0, allowedCut1)
if (
this.expectedRoundCut < DISTANCE_EPSILON ||
allowedCut < DISTANCE_EPSILON ||
this.cornerRadius < DISTANCE_EPSILON
) {
return [straightLine(this.p1.x, this.p1.y, this.p1.x, this.p1.y)]
}
const actualRoundCut = Math.min(allowedCut, this.expectedRoundCut)
const actualSmoothing0 = this.actualSmoothing(allowedCut0)
const actualSmoothing1 = this.actualSmoothing(allowedCut1)
const actualR = (this.cornerRadius * actualRoundCut) / this.expectedRoundCut
const centerDistance = Math.sqrt(actualR * actualR + actualRoundCut * actualRoundCut)
const center = plus(this.p1, times(direction(div(plus(this.d1, this.d2), 2)), centerDistance))
const circleIntersection0 = plus(this.p1, times(this.d1, actualRoundCut))
const circleIntersection2 = plus(this.p1, times(this.d2, actualRoundCut))
const flanking0 = this.flankingCurve(
actualRoundCut,
actualSmoothing0,
this.p1,
this.p0,
circleIntersection0,
circleIntersection2,
center,
actualR,
)
const flanking2 = reverse(
this.flankingCurve(
actualRoundCut,
actualSmoothing1,
this.p1,
this.p2,
circleIntersection2,
circleIntersection0,
center,
actualR,
),
)
return [
flanking0,
circularArc(center.x, center.y, flanking0[6], flanking0[7], flanking2[0], flanking2[1]),
flanking2,
]
}
actualSmoothing(allowedCut) {
if (allowedCut > this.expectedCut) {
return this.smoothing
}
if (allowedCut > this.expectedRoundCut) {
return (this.smoothing * (allowedCut - this.expectedRoundCut)) / (this.expectedCut - this.expectedRoundCut)
}
return 0
}
flankingCurve(actualRoundCut, smoothing, corner, sideStart, intersection, otherIntersection, circleCenter, actualR) {
const sideDirection = direction(minus(sideStart, corner))
const curveStart = plus(corner, times(sideDirection, actualRoundCut * (1 + smoothing)))
const p = lerpPoint(intersection, div(plus(intersection, otherIntersection), 2), smoothing)
const curveEnd = plus(circleCenter, times(direction(minus(p, circleCenter)), actualR))
const circleTangent = rotate90(minus(curveEnd, circleCenter))
const anchorEnd = lineIntersection(sideStart, sideDirection, curveEnd, circleTangent) ?? intersection
const anchorStart = div(plus(curveStart, times(anchorEnd, 2)), 3)
return cubic(curveStart, anchorStart, anchorEnd, curveEnd)
}
}
function lineIntersection(p0, d0, p1, d1) {
const rotatedD1 = rotate90(d1)
const den = dot(d0, rotatedD1)
if (Math.abs(den) < DISTANCE_EPSILON) {
return null
}
const num = dot(minus(p1, p0), rotatedD1)
if (Math.abs(den) < DISTANCE_EPSILON * Math.abs(num)) {
return null
}
return plus(p0, times(d0, num / den))
}
/**
* A polygon as androidx keeps it: its features (corners and the edges between them, each a
* list of cubics) and a centre. `cubics` flattens the features exactly as RoundedPolygon does.
*/
class RoundedPolygon {
constructor(features, center) {
this.features = features
this.center = center
this.cubics = flatten(features, center)
}
transformed(f) {
const move = (c) => {
const out = []
for (let i = 0; i < 8; i += 2) {
const p = f(c[i], c[i + 1])
out.push(p.x, p.y)
}
return out
}
return new RoundedPolygon(
this.features.map((feature) => ({ ...feature, cubics: feature.cubics.map(move) })),
f(this.center.x, this.center.y),
)
}
bounds(approximate) {
const all = this.cubics.map((c) => (zeroLength(c) ? [c[0], c[1], c[0], c[1]] : cubicBounds(c, approximate)))
return [
Math.min(...all.map((b) => b[0])),
Math.min(...all.map((b) => b[1])),
Math.max(...all.map((b) => b[2])),
Math.max(...all.map((b) => b[3])),
]
}
normalized() {
const [left, top, right, bottom] = this.bounds(true)
const width = right - left
const height = bottom - top
const side = Math.max(width, height)
const offsetX = (side - width) / 2 - left
const offsetY = (side - height) / 2 - top
return this.transformed((x, y) => point((x + offsetX) / side, (y + offsetY) / side))
}
}
function flatten(features, center) {
const out = []
let firstCubic = null
let lastCubic = null
let firstFeatureSplitStart = null
let firstFeatureSplitEnd = null
if (features.length > 0 && features[0].cubics.length === 3) {
const [start, end] = split(features[0].cubics[1], 0.5)
firstFeatureSplitStart = [features[0].cubics[0], start]
firstFeatureSplitEnd = [end, features[0].cubics[2]]
}
for (let i = 0; i <= features.length; i++) {
let featureCubics
if (i === 0 && firstFeatureSplitEnd !== null) {
featureCubics = firstFeatureSplitEnd
} else if (i === features.length) {
if (firstFeatureSplitStart === null) {
break
}
featureCubics = firstFeatureSplitStart
} else {
featureCubics = features[i].cubics
}
for (const c of featureCubics) {
if (!zeroLength(c)) {
if (lastCubic !== null) {
out.push(lastCubic)
}
lastCubic = c
if (firstCubic === null) {
firstCubic = c
}
} else if (lastCubic !== null) {
lastCubic = [...lastCubic]
lastCubic[6] = c[0]
lastCubic[7] = c[1]
}
}
}
if (lastCubic !== null && firstCubic !== null) {
out.push([...lastCubic.slice(0, 6), firstCubic[0], firstCubic[1]])
} else {
out.push([center.x, center.y, center.x, center.y, center.x, center.y, center.x, center.y])
}
for (let i = 0; i < out.length; i++) {
const previous = out[(i + out.length - 1) % out.length]
if (
Math.abs(out[i][0] - previous[6]) > DISTANCE_EPSILON ||
Math.abs(out[i][1] - previous[7]) > DISTANCE_EPSILON
) {
throw new Error('RoundedPolygon must be contiguous')
}
}
return out
}
/** RoundedPolygon(vertices, rounding, perVertexRounding, centerX, centerY) */
function polygonFromVertices(vertices, { cornerRounding = UNROUNDED, perVertexRounding = null, center = null } = {}) {
const n = vertices.length
if (n < 3) {
throw new Error('Polygons must have at least 3 vertices')
}
if (perVertexRounding !== null && perVertexRounding.length !== n) {
throw new Error('perVertexRounding list should be either null or the same size as the number of vertices')
}
const roundedCorners = vertices.map(
(vertex, i) =>
new RoundedCorner(vertices[(i + n - 1) % n], vertex, vertices[(i + 1) % n], perVertexRounding?.[i] ?? cornerRounding),
)
const cutAdjusts = vertices.map((vertex, i) => {
const next = (i + 1) % n
const expectedRoundCut = roundedCorners[i].expectedRoundCut + roundedCorners[next].expectedRoundCut
const expectedCut = roundedCorners[i].expectedCut + roundedCorners[next].expectedCut
const sideSize = length(minus(vertex, vertices[next]))
if (expectedRoundCut > sideSize) {
return [sideSize / expectedRoundCut, 0]
}
if (expectedCut > sideSize) {
return [1, (sideSize - expectedRoundCut) / (expectedCut - expectedRoundCut)]
}
return [1, 1]
})
const corners = roundedCorners.map((corner, i) => {
const allowedCuts = [0, 1].map((delta) => {
const [roundCutRatio, cutRatio] = cutAdjusts[(i + n - 1 + delta) % n]
return corner.expectedRoundCut * roundCutRatio + (corner.expectedCut - corner.expectedRoundCut) * cutRatio
})
return corner.getCubics(allowedCuts[0], allowedCuts[1])
})
const features = []
for (let i = 0; i < n; i++) {
const previous = vertices[(i + n - 1) % n]
const next = vertices[(i + 1) % n]
const end = corners[i].at(-1)
const start = corners[(i + 1) % n][0]
features.push({ type: 'corner', convex: convex(previous, vertices[i], next), cubics: corners[i] })
features.push({ type: 'edge', cubics: [straightLine(end[6], end[7], start[0], start[1])] })
}
if (center === null) {
center = point(
vertices.reduce((sum, v) => sum + v.x, 0) / n,
vertices.reduce((sum, v) => sum + v.y, 0) / n,
)
}
return new RoundedPolygon(features, center)
}
// Shapes.kt -----------------------------------------------------------------------------
/** RoundedPolygon(numVertices, radius, centerX, centerY, rounding, perVertexRounding) */
function regularPolygon(numVertices, { radius = 1, cornerRounding = UNROUNDED, perVertexRounding = null } = {}) {
const vertices = Array.from({ length: numVertices }, (_, i) =>
radialToCartesian(radius, (Math.PI / numVertices) * 2 * i),
)
return polygonFromVertices(vertices, { cornerRounding, perVertexRounding, center: point(0, 0) })
}
function circlePolygon(numVertices = 8, radius = 1) {
const polygonRadius = radius / Math.cos(Math.PI / numVertices)
return regularPolygon(numVertices, { radius: polygonRadius, cornerRounding: rounding(radius) })
}
function rectangle({ width = 2, height = 2, cornerRounding = UNROUNDED, perVertexRounding = null } = {}) {
const [left, top, right, bottom] = [-width / 2, -height / 2, width / 2, height / 2]
return polygonFromVertices([point(right, bottom), point(left, bottom), point(left, top), point(right, top)], {
cornerRounding,
perVertexRounding,
center: point(0, 0),
})
}
function star(numVerticesPerRadius, { radius = 1, innerRadius = 0.5, cornerRounding = UNROUNDED } = {}) {
const vertices = []
for (let i = 0; i < numVerticesPerRadius; i++) {
vertices.push(radialToCartesian(radius, (Math.PI / numVerticesPerRadius) * 2 * i))
vertices.push(radialToCartesian(innerRadius, (Math.PI / numVerticesPerRadius) * (2 * i + 1)))
}
return polygonFromVertices(vertices, { cornerRounding, center: point(0, 0) })
}
// Matrix.kt (rotateZ, scale) and ShapeUtil.kt (RoundedPolygon.transformed(Matrix)) --------
function rotateZ(degrees) {
const r = degrees * (Math.PI / 180)
const s = Math.sin(r)
const c = Math.cos(r)
return (x, y) => point(c * x - s * y, s * x + c * y)
}
const scale = (sx, sy) => (x, y) => point(x * sx, y * sy)
// MaterialShapes.kt ---------------------------------------------------------------------
const cornerRound15 = rounding(0.15)
const cornerRound20 = rounding(0.2)
const cornerRound30 = rounding(0.3)
const cornerRound50 = rounding(0.5)
const cornerRound100 = rounding(1)
const rotateNeg45 = rotateZ(-45)
const rotateNeg90 = rotateZ(-90)
const rotateNeg135 = rotateZ(-135)
/** PointNRound: a vertex of a custom polygon and its rounding. */
const pnr = (x, y, cornerRounding = UNROUNDED) => ({ o: point(x, y), r: cornerRounding })
const toRadians = (degrees) => (degrees / 360) * 2 * Math.PI
const angleDegrees = (p) => (Math.atan2(p.y, p.x) * 180) / Math.PI
function rotateDegrees(p, angle, center) {
const a = toRadians(angle)
const off = minus(p, center)
return plus(point(off.x * Math.cos(a) - off.y * Math.sin(a), off.x * Math.sin(a) + off.y * Math.cos(a)), center)
}
function doRepeat(points, reps, center, mirroring) {
if (!mirroring) {
const np = points.length
return Array.from({ length: np * reps }, (_, it) => ({
o: rotateDegrees(points[it % np].o, (Math.floor(it / np) * 360) / reps, center),
r: points[it % np].r,
}))
}
const angles = points.map((p) => angleDegrees(minus(p.o, center)))
const distances = points.map((p) => length(minus(p.o, center)))
const actualReps = reps * 2
const sectionAngle = 360 / actualReps
const out = []
for (let it = 0; it < actualReps; it++) {
for (let index = 0; index < points.length; index++) {
const i = it % 2 === 0 ? index : points.length - 1 - index
if (i > 0 || it % 2 === 0) {
const a = toRadians(
sectionAngle * it + (it % 2 === 0 ? angles[i] : sectionAngle - angles[i] + 2 * angles[0]),
)
out.push({ o: plus(times(point(Math.cos(a), Math.sin(a)), distances[i]), center), r: points[i].r })
}
}
}
return out
}
function customPolygon(points, reps, { center = point(0.5, 0.5), mirroring = false } = {}) {
const actualPoints = doRepeat(points, reps, center, mirroring)
return polygonFromVertices(
actualPoints.map((p) => p.o),
{ perVertexRounding: actualPoints.map((p) => p.r), center },
)
}
/** File name → the unnormalised shape, in MaterialShapes' declaration order. */
const SHAPES = {
circle: () => circlePolygon(10),
square: () => rectangle({ width: 1, height: 1, cornerRounding: cornerRound30 }),
slanted: () => customPolygon([pnr(0.926, 0.97, rounding(0.189, 0.811)), pnr(-0.021, 0.967, rounding(0.187, 0.057))], 2),
arch: () =>
regularPolygon(4, {
perVertexRounding: [cornerRound100, cornerRound100, cornerRound20, cornerRound20],
}).transformed(rotateNeg135),
fan: () =>
customPolygon(
[
pnr(1.004, 1.0, rounding(0.148, 0.417)),
pnr(0.0, 1.0, rounding(0.151)),
pnr(0.0, -0.003, rounding(0.148)),
pnr(0.978, 0.02, rounding(0.803)),
],
1,
),
arrow: () =>
customPolygon(
[
pnr(0.5, 0.892, rounding(0.313)),
pnr(-0.216, 1.05, rounding(0.207)),
pnr(0.499, -0.16, rounding(0.215, 1.0)),
pnr(1.225, 1.06, rounding(0.211)),
],
1,
),
'semi-circle': () =>
rectangle({
width: 1.6,
height: 1,
perVertexRounding: [cornerRound20, cornerRound20, cornerRound100, cornerRound100],
}),
oval: () => circlePolygon().transformed(scale(1, 0.64)).transformed(rotateNeg45),
pill: () =>
customPolygon([pnr(0.961, 0.039, rounding(0.426)), pnr(1.001, 0.428), pnr(1.0, 0.609, rounding(1.0))], 2, {
mirroring: true,
}),
triangle: () => regularPolygon(3, { cornerRounding: cornerRound20 }).transformed(rotateNeg90),
diamond: () => customPolygon([pnr(0.5, 1.096, rounding(0.151, 0.524)), pnr(0.04, 0.5, rounding(0.159))], 2),
'clam-shell': () =>
customPolygon(
[pnr(0.171, 0.841, rounding(0.159)), pnr(-0.02, 0.5, rounding(0.14)), pnr(0.17, 0.159, rounding(0.159))],
2,
),
pentagon: () =>
customPolygon(
[pnr(0.5, -0.009, rounding(0.172)), pnr(1.03, 0.365, rounding(0.164)), pnr(0.828, 0.97, rounding(0.169))],
1,
{ mirroring: true },
),
gem: () =>
customPolygon(
[
pnr(0.499, 1.023, rounding(0.241, 0.778)),
pnr(-0.005, 0.792, rounding(0.208)),
pnr(0.073, 0.258, rounding(0.228)),
pnr(0.433, -0.0, rounding(0.491)),
],
1,
{ mirroring: true },
),
'very-sunny': () => customPolygon([pnr(0.5, 1.08, rounding(0.085)), pnr(0.358, 0.843, rounding(0.085))], 8),
sunny: () => star(8, { innerRadius: 0.8, cornerRounding: cornerRound15 }),
'cookie-4': () => customPolygon([pnr(1.237, 1.236, rounding(0.258)), pnr(0.5, 0.918, rounding(0.233))], 4),
'cookie-6': () => customPolygon([pnr(0.723, 0.884, rounding(0.394)), pnr(0.5, 1.099, rounding(0.398))], 6),
'cookie-7': () => star(7, { innerRadius: 0.75, cornerRounding: cornerRound50 }).transformed(rotateNeg90),
'cookie-9': () => star(9, { innerRadius: 0.8, cornerRounding: cornerRound50 }).transformed(rotateNeg90),
'cookie-12': () => star(12, { innerRadius: 0.8, cornerRounding: cornerRound50 }).transformed(rotateNeg90),
ghostish: () =>
customPolygon(
[
pnr(0.5, 0, rounding(1.0)),
pnr(1, 0, rounding(1.0)),
pnr(1, 1.14, rounding(0.254, 0.106)),
pnr(0.575, 0.906, rounding(0.253)),
],
1,
{ mirroring: true },
),
'clover-4': () => customPolygon([pnr(0.5, 0.074), pnr(0.725, -0.099, rounding(0.476))], 4, { mirroring: true }),
'clover-8': () => customPolygon([pnr(0.5, 0.036), pnr(0.758, -0.101, rounding(0.209))], 8),
burst: () => customPolygon([pnr(0.5, -0.006, rounding(0.006)), pnr(0.592, 0.158, rounding(0.006))], 12),
'soft-burst': () => customPolygon([pnr(0.193, 0.277, rounding(0.053)), pnr(0.176, 0.055, rounding(0.053))], 10),
boom: () => customPolygon([pnr(0.457, 0.296, rounding(0.007)), pnr(0.5, -0.051, rounding(0.007))], 15),
'soft-boom': () =>
customPolygon(
[
pnr(0.733, 0.454),
pnr(0.839, 0.437, rounding(0.532)),
pnr(0.949, 0.449, rounding(0.439, 1.0)),
pnr(0.998, 0.478, rounding(0.174)),
],
16,
{ mirroring: true },
),
flower: () =>
customPolygon([pnr(0.37, 0.187), pnr(0.416, 0.049, rounding(0.381)), pnr(0.479, 0.001, rounding(0.095))], 8, {
mirroring: true,
}),
puffy: () =>
customPolygon(
[
pnr(0.5, 0.053),
pnr(0.545, -0.04, rounding(0.405)),
pnr(0.67, -0.035, rounding(0.426)),
pnr(0.717, 0.066, rounding(0.574)),
pnr(0.722, 0.128),
pnr(0.777, 0.002, rounding(0.36)),
pnr(0.914, 0.149, rounding(0.66)),
pnr(0.926, 0.289, rounding(0.66)),
pnr(0.881, 0.346),
pnr(0.94, 0.344, rounding(0.126)),
pnr(1.003, 0.437, rounding(0.255)),
],
2,
{ mirroring: true },
).transformed(scale(1, 0.742)),
'puffy-diamond': () =>
customPolygon([pnr(0.87, 0.13, rounding(0.146)), pnr(0.818, 0.357), pnr(1.0, 0.332, rounding(0.853))], 4, {
mirroring: true,
}),
'pixel-circle': () =>
customPolygon(
[
pnr(0.5, 0.0),
pnr(0.704, 0.0),
pnr(0.704, 0.065),
pnr(0.843, 0.065),
pnr(0.843, 0.148),
pnr(0.926, 0.148),
pnr(0.926, 0.296),
pnr(1.0, 0.296),
],
2,
{ mirroring: true },
),
'pixel-triangle': () =>
customPolygon(
[
pnr(0.11, 0.5),
pnr(0.113, 0.0),
pnr(0.287, 0.0),
pnr(0.287, 0.087),
pnr(0.421, 0.087),
pnr(0.421, 0.17),
pnr(0.56, 0.17),
pnr(0.56, 0.265),
pnr(0.674, 0.265),
pnr(0.675, 0.344),
pnr(0.789, 0.344),
pnr(0.789, 0.439),
pnr(0.888, 0.439),
],
1,
{ mirroring: true },
),
bun: () =>
customPolygon(
[pnr(0.796, 0.5), pnr(0.853, 0.518, rounding(1)), pnr(0.992, 0.631, rounding(1)), pnr(0.968, 1.0, rounding(1))],
2,
{ mirroring: true },
),
heart: () =>
customPolygon(
[
pnr(0.5, 0.268, rounding(0.016)),
pnr(0.792, -0.066, rounding(0.958)),
pnr(1.064, 0.276, rounding(1.0)),
pnr(0.501, 0.946, rounding(0.129)),
],
1,
{ mirroring: true },
),
}
// SVG -----------------------------------------------------------------------------------
/** Scales the normalised shape so its exact bounds span FILL on the larger axis, centred in the viewBox. */
function fitted(polygon) {
const [left, top, right, bottom] = polygon.bounds(false)
const k = FILL / Math.max(right - left, bottom - top)
const dx = VIEWBOX / 2 - ((left + right) / 2) * k
const dy = VIEWBOX / 2 - ((top + bottom) / 2) * k
return polygon.transformed((x, y) => point(x * k + dx, y * k + dy))
}
const round = (value) => Math.round(value * 10) / 10
function number(value) {
const rounded = round(value)
return String(Object.is(rounded, -0) ? 0 : rounded)
}
/**
* A cubic's curve stays inside the viewBox, but its control points can reach past it where a
* tight corner bulges towards the edge. Split such a cubic at its turning points (halving when it
* has none) until every control point is inside too: the same curve, drawn in more pieces.
*/
function contained(c, depth = 0) {
const outside = c.some((value) => round(value) < 0 || round(value) > VIEWBOX)
if (!outside || depth > 8) {
return [c]
}
const ts = [...new Set([...turningPoints(c, 0), ...turningPoints(c, 1)])]
.filter((t) => t > 1e-3 && t < 1 - 1e-3)
.sort((a, b) => a - b)
const cuts = ts.length > 0 ? ts : [0.5]
const pieces = []
let rest = c
let consumed = 0
for (const t of cuts) {
const [head, tail] = split(rest, (t - consumed) / (1 - consumed))
pieces.push(head)
rest = tail
consumed = t
}
pieces.push(rest)
return pieces.flatMap((piece) => contained(piece, depth + 1))
}
function pathData(polygon) {
const cubics = polygon.cubics.flatMap((c) => contained(c)).map((c) => c.map(number))
let d = `M${cubics[0][0]} ${cubics[0][1]}`
for (const c of cubics) {
// A corner rounded by a few thousandths collapses to a point at one decimal: skip it.
if (c.every((value, i) => value === c[i % 2])) {
continue
}
d += `C${c.slice(2).join(' ')}`
}
return `${d}Z`
}
mkdirSync(OUTPUT, { recursive: true })
for (const file of readdirSync(OUTPUT)) {
if (file.endsWith('.svg')) {
rmSync(join(OUTPUT, file))
}
}
for (const [name, build] of Object.entries(SHAPES)) {
const svg = `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 ${VIEWBOX} ${VIEWBOX}" fill="currentColor"><path d="${pathData(fitted(build().normalized()))}"/></svg>\n`
writeFileSync(join(OUTPUT, `${name}.svg`), svg)
}
console.log(`Wrote ${Object.keys(SHAPES).length} shapes to ${OUTPUT}`)
+1 -1
View File
@@ -22,11 +22,11 @@
], ],
"require": { "require": {
"php": "^8.4", "php": "^8.4",
"blade-ui-kit/blade-icons": "^1.10",
"laravel/framework": "^13.0", "laravel/framework": "^13.0",
"livewire/livewire": "^4.0" "livewire/livewire": "^4.0"
}, },
"require-dev": { "require-dev": {
"blade-ui-kit/blade-icons": "^1.10",
"laravel/pint": "^1.32", "laravel/pint": "^1.32",
"orchestra/testbench": "^11.2", "orchestra/testbench": "^11.2",
"pestphp/pest": "^5.1", "pestphp/pest": "^5.1",
+12
View File
@@ -34,6 +34,18 @@ return [
'legacy_keys' => [], 'legacy_keys' => [],
], ],
/*
|--------------------------------------------------------------------------
| Node
|--------------------------------------------------------------------------
|
| `php artisan material:scheme` runs Google's colour utilities through Node.
| Set the binary when `node` is not on the PATH of the user running Artisan.
|
*/
'node' => env('MATERIAL_NODE', 'node'),
/* /*
|-------------------------------------------------------------------------- |--------------------------------------------------------------------------
| Showcase | Showcase
+50 -6
View File
@@ -93,9 +93,12 @@ tokens, `training-row`, `map-shell`, `map-chip`, `product-shot`, `star-rating`,
- **CSS and JS imported from `vendor/`** (`@import`, `@source`, `import`) — one dependency, one - **CSS and JS imported from `vendor/`** (`@import`, `@source`, `import`) — one dependency, one
version; consuming Dockerfiles install Composer packages before the Vite build. version; consuming Dockerfiles install Composer packages before the Vite build.
`@source` covers the package's views and PHP, never its SVG folders. `@source` covers the package's views and PHP, never its SVG folders.
- **The full Material Symbols Rounded set** (400 / 0 / 24, outlined and filled, ~7,800 SVGs, - **The full Material Symbols Rounded set** (400 / 0 / 24, outlined and filled, 4,135 symbols,
~34 MB), produced by a maintenance script in the package repo, not fetched at runtime — 5.3 MB), fetched by a maintenance script in the package repo, never at runtime — any name works
any name works in any app; `icons:cache` keeps lookups cheap under Octane. in any app. **`<x-icon>` reads the SVG files itself; blade-icons is not used for them** (decided
after Phase 1): blade-icons registers one Blade component per icon whenever the view factory
resolves, ~8,000 registrations per request here. blade-icons stays a dev dependency, only to
test that the provider still keeps its `<x-icon>` from shadowing ours in apps that have it.
- **Google Sans Flex bundled** (ReStride's subset) via `@font-face` in the package CSS; an app - **Google Sans Flex bundled** (ReStride's subset) via `@font-face` in the package CSS; an app
overrides `--font-sans`. overrides `--font-sans`.
- **Full M3, not ReStride's doctrine** — every variant, colour role (tertiary and - **Full M3, not ReStride's doctrine** — every variant, colour role (tertiary and
@@ -273,6 +276,47 @@ tokens, `training-row`, `map-shell`, `map-chip`, `product-shot`, `star-rating`,
per foundation piece (colour roles in both themes, type scale, shapes, motion, icons per foundation piece (colour roles in both themes, type scale, shapes, motion, icons
search). search).
**Phase 2 is done (2026-09-13).** What changed from the steps above:
- **Icons without blade-icons** (see Decisions): `NoNameWeb\LivewireMaterial\Support\SvgFile`
reads a symbol or shape file on first use and keeps it for the worker. An unknown name throws,
and a Heroicon-style name (`o-home`) throws with a pointer to the catalogue. `bin/fetch-symbols`
sparse-checks-out google/material-design-icons (5 GB, cloned without blobs) for the
`_24px` and `_fill1_24px` files: 4,135 symbols, byte-identical to ReStride's hand-picked ones.
The `@material-symbols/svg-400` npm package was rejected: its "rounded" SVGs are optical size 48.
- **All 35 M3 Expressive shapes**, ported from androidx `MaterialShapes.kt` and graphics-shapes
into `bin/shapes.mjs` (commit in its header), each fitted to 298 of a 100-unit box; eight had
control points outside the box and are split into more segments along the same outline.
- **The scheme script** lives at `resources/node/scheme.mjs` (93 KB, esbuild bundle of
material-color-utilities 0.4.0, spec 2025 — M3 Expressive's colour; variants the 2025 spec
does not define fall back to 2021). The library's extensionless imports mean it cannot run
unbundled. 65 roles per theme plus `inverse-{error,success,warning,info}`. Default state
sources: success `#22a06b`, warning `#e2a400`, info `#1d7afc`. `config('livewire-material.node')`
names the binary. The package's own default is `#6750A4`, tonal-spot, in `tokens/scheme.css`.
- **Colour utilities are `@theme inline`** — contrary to ReStride's rule that `inline` breaks
theme switching. Verified in Chromium: without `inline`, `--color-primary` resolves once on
`:root`, so a `data-theme="dark"` section inside a light page keeps light colours; with it the
utility reads `var(--md-sys-color-primary)` on the element and both the page toggle and nested
themes work. Only blocks whose values are variables are inline. A browser test fails without it.
- **Tailwind emits only the theme variables that are used**, and cannot see a class assembled in
Blade (`type-{{ $style }}`): the showcase lists every class literally.
- **`dark:`** is keyed on `data-theme`, so it follows the page's theme rather than the OS.
- **Theme script** writes nothing for a visitor who never chose (a later change of
`theme.default` still reaches them); only an adopted legacy value is stored. `data-theme-choice`
and `data-theme-key` on `<html>` hand the state to `$store.theme` (`choice`, `resolved`, `set`,
`toggle`, `value`).
- **Moved out of Phase 2:** the snackbar listener goes to Phase 4 with `<x-toast>`; `data-list-row`
rows (JS and CSS) and the list keyboard go to Phase 5 with `list` and `card`, which they style.
`Toasts` dispatches `toast` with a 4 s default (M3's snackbar range is 410 s; ReStride used 3 s).
- **`DesignGuard`**: `secondary` is a valid role now that the package exposes full M3, so it is no
longer flagged as a daisyUI colour; icon attributes are checked per component tag (`icon` and
`icon-right` on the same tag).
- **Pest browser trap:** a bare `html` selector is taken as text to search for and times out;
assert on `document.documentElement` through `assertScript`. `@name` targets `data-test`.
- **Showcase**: colour roles in both themes side by side, type, corners and shapes, elevation,
springs and `x-figure`, and an icon search drawing matches as CSS masks from a showcase-only
symbol route (relative URLs — an absolute one carries `APP_URL` and misses the dev server's port).
### Phase 3 — Actions (`0.3.0`) ### Phase 3 — Actions (`0.3.0`)
19. Primitives the actions need: `loading` (M3 Expressive loading indicator, contained and 19. Primitives the actions need: `loading` (M3 Expressive loading indicator, contained and
@@ -370,7 +414,7 @@ Tracked in SealShare's `docs/plans/livewire-material.md`, after `1.0.0`.
normalisation (`false``null`); prefix config renames the tags. normalisation (`false``null`); prefix config renames the tags.
- **Foundation tests:** every `--md-sys-color-*` role defined in both theme blocks and - **Foundation tests:** every `--md-sys-color-*` role defined in both theme blocks and
different between them; each block declares `color-scheme`; no `prefers-color-scheme` in different between them; each block declares `color-scheme`; no `prefers-color-scheme` in
the CSS; `@theme` never `inline`; the scheme command writes both files for a known seed the CSS; colour blocks are `@theme inline`; the scheme command writes both files for a known seed
(snapshot of a few roles) and fails cleanly without Node; the theme script lands before (snapshot of a few roles) and fails cleanly without Node; the theme script lands before
`@vite` and resolves `system`; `<x-icon>` throws on an unknown name; every shape fills the `@vite` and resolves `system`; `<x-icon>` throws on an unknown name; every shape fills the
100-unit box with `currentColor` only; `Toasts` dispatches and survives `redirectTo`; 100-unit box with `currentColor` only; `Toasts` dispatches and survives `redirectTo`;
@@ -399,8 +443,8 @@ Tracked in SealShare's `docs/plans/livewire-material.md`, after `1.0.0`.
app records its own rules. app records its own rules.
- **Livewire morphing against Alpine state** inside components. Mitigation: ReStride's - **Livewire morphing against Alpine state** inside components. Mitigation: ReStride's
recorded traps become package rules in the skill, with tests pinning each. recorded traps become package rules in the skill, with tests pinning each.
- **~7,800 SVGs** make dist archives and `vendor/` larger (~4 MB) and a cold icon lookup - **~8,300 SVGs** make dist archives and `vendor/` larger (5.3 MB). Mitigation: each file is read
slower. Mitigation: `.gitattributes` keeps dev files out, `icons:cache` in deploys, `@source` only when drawn and kept per worker; `.gitattributes` keeps dev files out; `@source`
never scans the SVG folders. never scans the SVG folders.
- **Maintenance of a public package** is one person's job; breaking changes need semver - **Maintenance of a public package** is one person's job; breaking changes need semver
discipline once ReStride also depends on it. discipline once ReStride also depends on it.
+493
View File
@@ -5,13 +5,457 @@
"packages": { "packages": {
"": { "": {
"devDependencies": { "devDependencies": {
"@material/material-color-utilities": "^0.4.0",
"@tailwindcss/vite": "^4.3.3", "@tailwindcss/vite": "^4.3.3",
"esbuild": "^0.28.2",
"laravel-vite-plugin": "^3.2.0", "laravel-vite-plugin": "^3.2.0",
"playwright": "^1.62.1", "playwright": "^1.62.1",
"tailwindcss": "^4.3.3", "tailwindcss": "^4.3.3",
"vite": "^8.2.2" "vite": "^8.2.2"
} }
}, },
"node_modules/@esbuild/aix-ppc64": {
"version": "0.28.2",
"resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.28.2.tgz",
"integrity": "sha512-XExcO+dvLKvVtNTibSTBej1NCAbaGhWn9Ww1ZPx80qsahhPFe/8jgWP0IchNe0F3HwkU7n8ejhH8bjonqht8mQ==",
"cpu": [
"ppc64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"aix"
],
"engines": {
"node": ">=18"
}
},
"node_modules/@esbuild/android-arm": {
"version": "0.28.2",
"resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.28.2.tgz",
"integrity": "sha512-kXXoiPVVGQcnIYGOeaovwOURpniDBpSq4A03qkQ+BMQqtGG6HYap3xne9C1O1yo4TR3qxlCX5IqqmX6fFo2Lqg==",
"cpu": [
"arm"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"android"
],
"engines": {
"node": ">=18"
}
},
"node_modules/@esbuild/android-arm64": {
"version": "0.28.2",
"resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.28.2.tgz",
"integrity": "sha512-5YfKeeI8qWfBZIX+u2xZC3Zlb3Os/gLS2sbEKM+I4ZOcsWmHS2WLysCcQZDAFRslDUU5Oiq44gf6PYN1vGwG5A==",
"cpu": [
"arm64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"android"
],
"engines": {
"node": ">=18"
}
},
"node_modules/@esbuild/android-x64": {
"version": "0.28.2",
"resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.28.2.tgz",
"integrity": "sha512-O387ite7SzUyCcy3JQX4P4bLtEA7bLLkx+esve5JHnyYfNTxcVpXZo9jhdB0lTKN44gztELTdU7nS8Nr16Fs1Q==",
"cpu": [
"x64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"android"
],
"engines": {
"node": ">=18"
}
},
"node_modules/@esbuild/darwin-arm64": {
"version": "0.28.2",
"resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.28.2.tgz",
"integrity": "sha512-n4KqkOQrraxHJcgjM1RvwbigfQKIKJVpM7xp+KsxiyUSrRdIXnt73VhrPAx0fV44hgfmIVKjxMN9J1t5jySVkw==",
"cpu": [
"arm64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"darwin"
],
"engines": {
"node": ">=18"
}
},
"node_modules/@esbuild/darwin-x64": {
"version": "0.28.2",
"resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.28.2.tgz",
"integrity": "sha512-uq6suIWYP37qzGddBKPw5QEQPi6HiLGsO7UmkpfyaYNQ3D+rN6w6WfwH+nuqcGXWvawGwxOEroO4YGnFh95azw==",
"cpu": [
"x64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"darwin"
],
"engines": {
"node": ">=18"
}
},
"node_modules/@esbuild/freebsd-arm64": {
"version": "0.28.2",
"resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.28.2.tgz",
"integrity": "sha512-n+I0BTSRIoy+d6RPKnEVwql5UwBJolytvY4mAOIEJorKlqgPII8ix6slVVrfZ5Tnj7glIZvloylbB/EJPMWEXw==",
"cpu": [
"arm64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"freebsd"
],
"engines": {
"node": ">=18"
}
},
"node_modules/@esbuild/freebsd-x64": {
"version": "0.28.2",
"resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.28.2.tgz",
"integrity": "sha512-78XJTJkvPs0kz2w61301PJjXl4g7q3JqiYMZ/M/yVI73EHBrCRTgkhu9oqG7vPqq+a/yadEW8aD+agKlk5xrmg==",
"cpu": [
"x64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"freebsd"
],
"engines": {
"node": ">=18"
}
},
"node_modules/@esbuild/linux-arm": {
"version": "0.28.2",
"resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.28.2.tgz",
"integrity": "sha512-XlDnu2q5yoqems+xay6wSAcg9DDD7K9RLKZEBOMZm3ckNpJBvOX20tSfby8KfrrhINDyv9V2YVZKY/SpoGJI8w==",
"cpu": [
"arm"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">=18"
}
},
"node_modules/@esbuild/linux-arm64": {
"version": "0.28.2",
"resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.28.2.tgz",
"integrity": "sha512-pW4AC0P3it8c7do9MVM4p51FzHzdM/TZrerurgRcHJ2WTa1VQ1CIq18xncfpBJw4ojkiZZrKW2yIBWBP92j6Ug==",
"cpu": [
"arm64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">=18"
}
},
"node_modules/@esbuild/linux-ia32": {
"version": "0.28.2",
"resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.28.2.tgz",
"integrity": "sha512-CYbnj78HsIeA+DhgUKgFCfvNsTHFhMMrinUrMZpDXJXKN8T3XViTZ/+wtHeVxEWY8ewSzTFN+nRmSwO2tZaLUQ==",
"cpu": [
"ia32"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">=18"
}
},
"node_modules/@esbuild/linux-loong64": {
"version": "0.28.2",
"resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.28.2.tgz",
"integrity": "sha512-buwkd8nsph4R+ajRvw0qM5Hja/TXQow3ptzWO2EbG/cqcIkHloRrdlBtQlshyYGTNFvfkfJ5tpPLVkY4DtsPfQ==",
"cpu": [
"loong64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">=18"
}
},
"node_modules/@esbuild/linux-mips64el": {
"version": "0.28.2",
"resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.28.2.tgz",
"integrity": "sha512-ZVykbDyk7519VwiNb9Lcj9m8XM6v5V9uKPvrEMkkEedVewf+0itkhahp4HDpgERXhwLRpWFypsGbG/J8s0QjJA==",
"cpu": [
"mips64el"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">=18"
}
},
"node_modules/@esbuild/linux-ppc64": {
"version": "0.28.2",
"resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.28.2.tgz",
"integrity": "sha512-CAXl+Dtd9UUuJd8pKKdwh6MLm3MUMiqMPmhZ3tTSXPqfyQ3vDl6R5hZdZ/kYojK4ofXtdfSv1tFq8XzWx3heNQ==",
"cpu": [
"ppc64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">=18"
}
},
"node_modules/@esbuild/linux-riscv64": {
"version": "0.28.2",
"resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.28.2.tgz",
"integrity": "sha512-GeXCej4IQtU1B+QlDV8W/RRvbzI3O/Stss+/bCXv4lZls5WGRtu2a+3JkA3i4qIUlMXpcHebWpF8AkJhATowuA==",
"cpu": [
"riscv64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">=18"
}
},
"node_modules/@esbuild/linux-s390x": {
"version": "0.28.2",
"resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.28.2.tgz",
"integrity": "sha512-3H1weTYZPxt/WOhByszQZybS9w5lKzUn1FDMsgEChbHWQwHYQQRfBxgCcZvPhjHfKyJjIievvMmEUawJrdY9Dg==",
"cpu": [
"s390x"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">=18"
}
},
"node_modules/@esbuild/linux-x64": {
"version": "0.28.2",
"resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.28.2.tgz",
"integrity": "sha512-4xTZr1FUmSoQW4XIWmit3tzQrUTZM+N3P0XV8xROKYF50XfI7xeO90+1bZvNwxIufQ9hDQVRJH5YhgPVF8A/HQ==",
"cpu": [
"x64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">=18"
}
},
"node_modules/@esbuild/netbsd-arm64": {
"version": "0.28.2",
"resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.28.2.tgz",
"integrity": "sha512-sSATRjPeDBg3pdgHoQfoYBob11Kk1FGa9lui5RIHZCoCkJa9QKlvl3/vKz2usCmYYjs7ymJR/2Nnsqe+Hjt5nw==",
"cpu": [
"arm64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"netbsd"
],
"engines": {
"node": ">=18"
}
},
"node_modules/@esbuild/netbsd-x64": {
"version": "0.28.2",
"resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.28.2.tgz",
"integrity": "sha512-lqnzCV+mM0gIADaKihiCg6ifgfU2L3h5E33rNQBN1Y4MaVGnzryzmvvf7UHxprpQdE8hpqLolJ9Rl+SkIRDpyw==",
"cpu": [
"x64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"netbsd"
],
"engines": {
"node": ">=18"
}
},
"node_modules/@esbuild/openbsd-arm64": {
"version": "0.28.2",
"resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.28.2.tgz",
"integrity": "sha512-AL2qJILH7lNjrDmCQDvdxMfAUIv8KMNZOvrwAQ8i8//ntL9FflhOyMJ8OZSMBb8/AWXe3/5v5S20y3zCoZWKoQ==",
"cpu": [
"arm64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"openbsd"
],
"engines": {
"node": ">=18"
}
},
"node_modules/@esbuild/openbsd-x64": {
"version": "0.28.2",
"resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.28.2.tgz",
"integrity": "sha512-QtiuPytchRyC4rwUKhexJdQKvDuZ6hWloi3igqPQNUJCS1/v9EiO3UTOXR6A3FoMo4fnAKbWJdqaIwhOzh8qEw==",
"cpu": [
"x64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"openbsd"
],
"engines": {
"node": ">=18"
}
},
"node_modules/@esbuild/openharmony-arm64": {
"version": "0.28.2",
"resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.28.2.tgz",
"integrity": "sha512-WkhYDmpTjLvGlScA1rwjRUmhl4k8oXR3cIbtqWmELgU/dFeHHlEllxDvdWcNJV9rbzCexB5vz8gtNewWLgCT7Q==",
"cpu": [
"arm64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"openharmony"
],
"engines": {
"node": ">=18"
}
},
"node_modules/@esbuild/sunos-x64": {
"version": "0.28.2",
"resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.28.2.tgz",
"integrity": "sha512-GPMSkTOtMnv2U2F8gxe4Io6qmVs+YKyp832Etqqxr0hFngmXQ3rzwytelm3GIn7T4VviRUlf3sOgBOiTdvaf7g==",
"cpu": [
"x64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"sunos"
],
"engines": {
"node": ">=18"
}
},
"node_modules/@esbuild/win32-arm64": {
"version": "0.28.2",
"resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.28.2.tgz",
"integrity": "sha512-PIhhEkE9uPBleRBrQEJpUn7MBnibZzbGzYWPmY3x+YoVg/95zbjB4CxPPOQ8l5tYYM4mMaCthF8/1DIfBQQyWQ==",
"cpu": [
"arm64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"win32"
],
"engines": {
"node": ">=18"
}
},
"node_modules/@esbuild/win32-ia32": {
"version": "0.28.2",
"resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.28.2.tgz",
"integrity": "sha512-YmJbfTlvU7Sdn9BB+4PRES4oB6pxgS37MAONj+hBr/cpXS1aBPKXxNnDbu+QCWPj0o9dgyxeq79g6c5P8KeuYA==",
"cpu": [
"ia32"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"win32"
],
"engines": {
"node": ">=18"
}
},
"node_modules/@esbuild/win32-x64": {
"version": "0.28.2",
"resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.28.2.tgz",
"integrity": "sha512-5ebpxr3nWMzrL/rnUI755Jkuee0bHL/Gq0WTF9lvcpv73wAp5eu8MfBUgWK9bhWvZjj7yX8etf/8tI8Ney695g==",
"cpu": [
"x64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"win32"
],
"engines": {
"node": ">=18"
}
},
"node_modules/@jridgewell/gen-mapping": { "node_modules/@jridgewell/gen-mapping": {
"version": "0.3.13", "version": "0.3.13",
"resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.13.tgz", "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.13.tgz",
@@ -62,6 +506,13 @@
"@jridgewell/sourcemap-codec": "^1.4.14" "@jridgewell/sourcemap-codec": "^1.4.14"
} }
}, },
"node_modules/@material/material-color-utilities": {
"version": "0.4.0",
"resolved": "https://registry.npmjs.org/@material/material-color-utilities/-/material-color-utilities-0.4.0.tgz",
"integrity": "sha512-dlq6VExJReb8dhjj3a/yTigr3ncNwoFmL5Iy2ENtbDX03EmNeOEdZ+vsaGrj7RTuO+mB7L58II4LCsl4NpM8uw==",
"dev": true,
"license": "Apache-2.0"
},
"node_modules/@oxc-project/types": { "node_modules/@oxc-project/types": {
"version": "0.149.0", "version": "0.149.0",
"resolved": "https://registry.npmjs.org/@oxc-project/types/-/types-0.149.0.tgz", "resolved": "https://registry.npmjs.org/@oxc-project/types/-/types-0.149.0.tgz",
@@ -660,6 +1111,48 @@
"node": ">=10.13.0" "node": ">=10.13.0"
} }
}, },
"node_modules/esbuild": {
"version": "0.28.2",
"resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.28.2.tgz",
"integrity": "sha512-HKVLS8dvII+xoKW9kmqxbRKrnWEXfJJr/FZhhJmiqIB0e053QNYFqOBouTMO/k5sID4MvCiUCvv8b9M4h32wIA==",
"dev": true,
"hasInstallScript": true,
"license": "MIT",
"bin": {
"esbuild": "bin/esbuild"
},
"engines": {
"node": ">=18"
},
"optionalDependencies": {
"@esbuild/aix-ppc64": "0.28.2",
"@esbuild/android-arm": "0.28.2",
"@esbuild/android-arm64": "0.28.2",
"@esbuild/android-x64": "0.28.2",
"@esbuild/darwin-arm64": "0.28.2",
"@esbuild/darwin-x64": "0.28.2",
"@esbuild/freebsd-arm64": "0.28.2",
"@esbuild/freebsd-x64": "0.28.2",
"@esbuild/linux-arm": "0.28.2",
"@esbuild/linux-arm64": "0.28.2",
"@esbuild/linux-ia32": "0.28.2",
"@esbuild/linux-loong64": "0.28.2",
"@esbuild/linux-mips64el": "0.28.2",
"@esbuild/linux-ppc64": "0.28.2",
"@esbuild/linux-riscv64": "0.28.2",
"@esbuild/linux-s390x": "0.28.2",
"@esbuild/linux-x64": "0.28.2",
"@esbuild/netbsd-arm64": "0.28.2",
"@esbuild/netbsd-x64": "0.28.2",
"@esbuild/openbsd-arm64": "0.28.2",
"@esbuild/openbsd-x64": "0.28.2",
"@esbuild/openharmony-arm64": "0.28.2",
"@esbuild/sunos-x64": "0.28.2",
"@esbuild/win32-arm64": "0.28.2",
"@esbuild/win32-ia32": "0.28.2",
"@esbuild/win32-x64": "0.28.2"
}
},
"node_modules/fdir": { "node_modules/fdir": {
"version": "6.5.0", "version": "6.5.0",
"resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz", "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz",
+5 -1
View File
@@ -4,10 +4,14 @@
"type": "module", "type": "module",
"scripts": { "scripts": {
"build": "vite build", "build": "vite build",
"dev": "vite" "dev": "vite",
"build:scheme": "esbuild bin/scheme.mjs --bundle --platform=node --format=esm --target=node20 --minify --legal-comments=eof --outfile=resources/node/scheme.mjs",
"build:shapes": "node bin/shapes.mjs"
}, },
"devDependencies": { "devDependencies": {
"@material/material-color-utilities": "^0.4.0",
"@tailwindcss/vite": "^4.3.3", "@tailwindcss/vite": "^4.3.3",
"esbuild": "^0.28.2",
"laravel-vite-plugin": "^3.2.0", "laravel-vite-plugin": "^3.2.0",
"playwright": "^1.62.1", "playwright": "^1.62.1",
"tailwindcss": "^4.3.3", "tailwindcss": "^4.3.3",
+2 -1
View File
@@ -5,5 +5,6 @@ This application uses `nonameweb/livewire-material`: Material 3 Expressive compo
- Components are anonymous Blade components, unprefixed unless `config/livewire-material.php` sets a `prefix`. Before writing or changing a view that uses them, activate the `livewire-material-development` skill for the props, slots and traps of each component. - Components are anonymous Blade components, unprefixed unless `config/livewire-material.php` sets a `prefix`. Before writing or changing a view that uses them, activate the `livewire-material-development` skill for the props, slots and traps of each component.
- Never write maryUI tags (`<x-mary-*>`) or daisyUI classes (`btn`, `card`, `badge`, `bg-base-200`, `text-base-content`). They compile to nothing and fail silently. - Never write maryUI tags (`<x-mary-*>`) or daisyUI classes (`btn`, `card`, `badge`, `bg-base-200`, `text-base-content`). They compile to nothing and fail silently.
- While the application runs locally, every component renders in the application's own scheme at `/material` (the showcase). - Every layout includes `<x-theme-script />` in `<head>` before `@vite`. The colour scheme is generated with `php artisan material:scheme` never edit `resources/css/material-scheme.css` by hand.
- While the application runs locally, every token and component renders in the application's own scheme at `/material` (the showcase).
@endverbatim @endverbatim
@@ -1,6 +1,6 @@
--- ---
name: livewire-material-development name: livewire-material-development
description: Build Laravel and Livewire views with Livewire Material's Material 3 Expressive Blade components — props, slots, theming and the Livewire traps each component handles. description: Build Laravel and Livewire views with Livewire Material's Material 3 Expressive Blade components — props, slots, colour roles, type, shape, motion, theming, toasts, the design guard, and the Livewire traps each component handles.
--- ---
# Livewire Material Development # Livewire Material Development
@@ -11,12 +11,13 @@ Use this skill when writing or changing any Blade view, Livewire component view
## Setup ## Setup
The package ships CSS and JavaScript that the application imports from `vendor/`, after Tailwind: Composer packages must be installed before the Vite build (in Dockerfiles and CI alike), because the application's build imports from `vendor/`:
```css ```css
/* resources/css/app.css */ /* resources/css/app.css */
@import 'tailwindcss'; @import 'tailwindcss';
@import '../../vendor/nonameweb/livewire-material/resources/css/material.css'; @import '../../vendor/nonameweb/livewire-material/resources/css/material.css';
@import './material-scheme.css';
@source '../../vendor/nonameweb/livewire-material/resources/views'; @source '../../vendor/nonameweb/livewire-material/resources/views';
@source '../../vendor/nonameweb/livewire-material/src'; @source '../../vendor/nonameweb/livewire-material/src';
``` ```
@@ -26,16 +27,105 @@ The package ships CSS and JavaScript that the application imports from `vendor/`
import '../../vendor/nonameweb/livewire-material/resources/js/material.js' import '../../vendor/nonameweb/livewire-material/resources/js/material.js'
``` ```
Composer packages must be installed before the Vite build (in Dockerfiles and CI alike), or these imports have nothing to read. Every layout puts the theme script in `<head>`, before `@vite`:
```blade
<head>
<x-theme-script />
@vite(['resources/css/app.css', 'resources/js/app.js'])
</head>
```
## Colour scheme
The scheme is generated, never hand-edited. Regenerate it with the seed and variant recorded at the top of `resources/css/material-scheme.css`:
```bash
php artisan material:scheme "#4f46e5" --variant=tonal-spot
```
Variants: `tonal-spot` (M3's default), `vibrant`, `expressive`, `neutral`, `fidelity`, `content`, `monochrome`, `rainbow`, `fruit-salad`. `--success`, `--warning` and `--info` set the source of the state colours; `--contrast` goes from -1 to 1. The command also writes `material-scheme.json` beside the stylesheet.
## Tokens
Tailwind's default palette is cleared: every colour class names an M3 role. `text-red-600`, `bg-base-200` or `text-gray-500` compile to nothing.
- Colour roles (`bg-*`, `text-*`, `border-*`, …): `primary`, `on-primary`, `primary-container`, `on-primary-container`, `inverse-primary`, `primary-fixed`, `primary-fixed-dim`, `on-primary-fixed`, `on-primary-fixed-variant`; the same for `secondary` and `tertiary`; `error`, `on-error`, `error-container`, `on-error-container`; `success`, `warning` and `info` with their `on-`, `-container` and `on-…-container`; `inverse-error|success|warning|info`; `surface`, `surface-dim`, `surface-bright`, `surface-container-lowest|low||high|highest`, `on-surface`, `on-surface-variant`, `inverse-surface`, `inverse-on-surface`, `outline`, `outline-variant`, `scrim`, `shadow`; plus `white` and `black`.
- Ink and lines by meaning: `text-body` (body copy), `text-meta` (metadata), `text-quiet` (decoration only), `border-structure`, `border-chrome`, `border-divider` / `divide-divider`.
- Type: `type-{display|headline|title|body|label}-{lg|md|sm}` and `type-emphasized-…`. Never assemble `text-*`, `leading-*` and `tracking-*` by hand. The font is Google Sans Flex (`font-sans`).
- Shape: `rounded-corner-{none|xs|sm|md|lg|lg-increased|xl|xl-increased|xxl|full}`.
- Elevation: `shadow-elevation-{1…5}` — for what floats over content, not for panels (a panel separates by its container tone).
- Motion: `ease-spatial-{fast|default|slow}` (position, size, shape; springs that overshoot) and `ease-effects-{fast|default|slow}` (colour, opacity). Always pair an easing with its duration: `duration-(--md-sys-motion-spatial-fast-duration) ease-spatial-fast`. Reduced motion zeroes the durations.
- States: `state-layer` (M3's hover/focus/press overlay; makes the element `relative` and `isolate`), `focus-ring` (keyboard focus indicator), `link` (a link in running text).
- `dark:` follows the page's theme (`data-theme`), not the operating system.
- `x-figure` on an element holding one number counts it up on first appearance and on change.
## Theme
`config/livewire-material.php``theme.default` (`light`, `dark` or `system`), `theme.storage_key`, `theme.legacy_keys`. In Alpine, `$store.theme` holds `choice` (what the visitor picked), `resolved` (`light` or `dark`, what shows), `set('light'|'dark'|'system')` and `toggle()`; `x-model="$store.theme.value"` binds a control.
## Toasts
```php
use NoNameWeb\LivewireMaterial\Concerns\Toasts;
class Settings extends Component
{
use Toasts;
public function save(): void
{
// …
$this->success('Settings saved'); // also warning(), error(), info()
$this->info('Link copied', timeout: 6000);
$this->success('Share created', redirectTo: route('shares.show', $share));
}
}
```
The methods are protected. They dispatch a `toast` browser event (`assertDispatched('toast', type: 'success', title: 'Settings saved')` in tests).
## Components
### `<x-icon>`
A Material Symbol (Rounded, weight 400, grade 0, 24px), inline. Every symbol on fonts.google.com/icons exists, by Google's name with underscores. An unknown name throws.
| Prop | Default | |
|---|---|---|
| `name` | required | `calendar_month`, `cloud_upload`, `content_copy` |
| `filled` | `false` | the filled symbol — M3 uses it for active or selected |
| `label` | `null` | names the icon for screen readers when it carries the meaning alone; otherwise it is `aria-hidden` |
24px (`size-6`) unless a `size-*`, `w-*` or `h-*` class is passed. Colour follows the text: `<x-icon name="lock" class="size-5 text-on-surface-variant" />`.
### `<x-shape>`
One of M3 Expressive's 35 shapes, filled in the text colour, `aria-hidden`, sized by its caller: `<x-shape name="cookie-9" class="size-40 text-secondary-container" />`. Names: `circle`, `square`, `slanted`, `arch`, `fan`, `arrow`, `semi-circle`, `oval`, `pill`, `triangle`, `diamond`, `clam-shell`, `pentagon`, `gem`, `very-sunny`, `sunny`, `cookie-4`, `cookie-6`, `cookie-7`, `cookie-9`, `cookie-12`, `ghostish`, `clover-4`, `clover-8`, `burst`, `soft-burst`, `boom`, `soft-boom`, `flower`, `puffy`, `puffy-diamond`, `pixel-circle`, `pixel-triangle`, `bun`, `heart`.
### `<x-theme-script>`
The theme decided before the first paint. Exactly once per layout, in `<head>`, before `@vite`. No props; configured in `config/livewire-material.php`.
## Testing the design
```php
use NoNameWeb\LivewireMaterial\Testing\DesignGuard;
it('uses only what compiles', function () {
expect(DesignGuard::scan([resource_path('views'), resource_path('js'), app_path()])
->forbidColours(['tertiary']) // roles this application's rules leave out
->violations())->toBe([]);
});
```
It fails on maryUI tags, daisyUI classes, colours the theme does not declare and unknown symbol names, with `path:line` for each.
## Conventions ## Conventions
- Components are anonymous Blade components: `<x-name>` without a prefix, or `<x-{prefix}name>` when `config('livewire-material.prefix')` is set. - Components are anonymous Blade components: `<x-name>` without a prefix, or `<x-{prefix}name>` when `config('livewire-material.prefix')` is set.
- The showcase at `/material` (local only, `MATERIAL_SHOWCASE` to force it) renders every component with its Blade snippet. - Write class names out whole. Tailwind cannot compile `'text-'.$tone` or `type-{{ $size }}`, and the design guard cannot read them.
- The showcase at `/material` (local only, `MATERIAL_SHOWCASE=true` to force it) renders every token and component.
## Components
Each component the package ships is listed here with its props and slots.
## Livewire traps ## Livewire traps
+21 -2
View File
@@ -1,10 +1,29 @@
/* /*
* Livewire Material — the one stylesheet an application imports, after Tailwind: * Livewire Material — the one stylesheet an application imports, after Tailwind, followed by
* the scheme `php artisan material:scheme` wrote for it:
* *
* @import 'tailwindcss'; * @import 'tailwindcss';
* @import '../../vendor/nonameweb/livewire-material/resources/css/material.css'; * @import '../../vendor/nonameweb/livewire-material/resources/css/material.css';
* @import './material-scheme.css';
* @source '../../vendor/nonameweb/livewire-material/resources/views'; * @source '../../vendor/nonameweb/livewire-material/resources/views';
* @source '../../vendor/nonameweb/livewire-material/src'; * @source '../../vendor/nonameweb/livewire-material/src';
* *
* Colour roles, shape, type, motion, elevation and the component styles arrive in 0.2.0. * tokens/scheme.css is the package's own default (M3's baseline purple), so an application
* renders before it has a scheme; the application's file declares the same roles later and wins.
*/ */
@import './tokens/scheme.css';
@import './tokens/shape.css';
@import './tokens/elevation.css';
@import './tokens/motion.css';
@import './tokens/type.css';
@import './tokens/font.css';
@import './tokens/theme.css';
@import './tokens/state.css';
@layer base {
html {
background-color: var(--md-sys-color-surface);
color: var(--md-sys-color-on-surface);
}
}
+21
View File
@@ -0,0 +1,21 @@
/*
* Elevation, as M3's five shadow levels.
*
* Geometry from @material/web's token files (Apache-2.0, © Google LLC). The colour goes
* through `color-mix()` rather than their relative `hsl(from …)`, which needs a newer
* browser for the same result.
*
* Shadows are for things that float over content — menus, the FAB, a floating toolbar, a
* sheet. On a dark surface a shadow is nearly invisible, so a panel separates from the page
* by its container tone instead. As utilities: shadow-elevation-1 … shadow-elevation-5.
*/
:root {
--md-sys-color-shadow: #000000;
--md-sys-color-scrim: #000000;
--md-sys-elevation-1: 0 1px 2px 0 color-mix(in srgb, var(--md-sys-color-shadow) 30%, transparent), 0 1px 3px 1px color-mix(in srgb, var(--md-sys-color-shadow) 15%, transparent);
--md-sys-elevation-2: 0 1px 2px 0 color-mix(in srgb, var(--md-sys-color-shadow) 30%, transparent), 0 2px 6px 2px color-mix(in srgb, var(--md-sys-color-shadow) 15%, transparent);
--md-sys-elevation-3: 0 1px 3px 0 color-mix(in srgb, var(--md-sys-color-shadow) 30%, transparent), 0 4px 8px 3px color-mix(in srgb, var(--md-sys-color-shadow) 15%, transparent);
--md-sys-elevation-4: 0 2px 3px 0 color-mix(in srgb, var(--md-sys-color-shadow) 30%, transparent), 0 6px 10px 4px color-mix(in srgb, var(--md-sys-color-shadow) 15%, transparent);
--md-sys-elevation-5: 0 4px 4px 0 color-mix(in srgb, var(--md-sys-color-shadow) 30%, transparent), 0 8px 12px 6px color-mix(in srgb, var(--md-sys-color-shadow) 15%, transparent);
}
+18
View File
@@ -0,0 +1,18 @@
/*
* Google Sans Flex (SIL Open Font License 1.1, resources/fonts/google-sans-flex/OFL.txt).
*
* One variable woff2, cut down with fontTools: Latin and Latin Extended plus a handful of
* symbols (≈ →), weight 400700 and roundness 0100 kept, width, grade, slant and optical
* size pinned — 63 KB where the full font is 4 MB. Vite copies it into the application's
* build from this relative url.
*
* An application that wants another typeface overrides `--md-ref-typeface-brand` (and so
* `font-sans`) after importing material.css.
*/
@font-face {
font-family: 'Google Sans Flex';
src: url('../../fonts/google-sans-flex/GoogleSansFlex-Latin.woff2') format('woff2');
font-weight: 400 700;
font-style: normal;
font-display: swap;
}
+75
View File
@@ -0,0 +1,75 @@
/*
* Motion, as M3 Expressive's springs.
*
* M3 Expressive moves on physics, not on a duration and a curve: every transition is a
* spring with a damping ratio and a stiffness. CSS cannot run a spring, so each one is
* sampled into a `linear()` easing over the time it takes to settle (to within 0.1% of its
* travel) — 49 points, past the resolution anyone can see at 60 Hz. The constants are
* androidx Compose Material 3's own (tokens/ExpressiveMotionTokens.kt, Apache-2.0):
*
* spatial — position, size, shape. Underdamped, so the fast one overshoots by 9% and
* settles back: that small bounce is what reads as Expressive.
* effects — colour and opacity. Critically damped; a colour must never overshoot.
*
* Use a pair together, easing and duration, or the curve is stretched over the wrong time
* and the bounce lands late:
*
* transition: transform var(--md-sys-motion-spatial-fast-duration) var(--md-sys-motion-spatial-fast);
* class="transition-transform duration-(--md-sys-motion-spatial-fast-duration) ease-spatial-fast"
*
* The cubic-bezier easings below are M3's non-spring set (@material/web token values,
* Apache-2.0, © Google LLC), for the few places a duration is fixed by something outside
* the page — a view transition, an animated scroll.
*
* Reduced motion zeroes every duration here, so anything that animates through these tokens
* turns instant with no per-component check; anything that animates without them is a bug.
*/
:root {
/* damping 0.6, stiffness 800; settles in 360ms, peaks at 1.094 */
--md-sys-motion-spatial-fast: linear(0, 0.0206, 0.0754, 0.1544, 0.2492, 0.3524, 0.458, 0.5613, 0.6588, 0.7479, 0.8269, 0.895, 0.952, 0.9981, 1.0338, 1.0603, 1.0783, 1.0893, 1.0942, 1.0943, 1.0906, 1.0842, 1.0759, 1.0665, 1.0565, 1.0465, 1.037, 1.0281, 1.0201, 1.0131, 1.0072, 1.0023, 0.9984, 0.9955, 0.9934, 0.992, 0.9912, 0.991, 0.9912, 0.9917, 0.9924, 0.9932, 0.9942, 0.9951, 0.996, 0.9969, 0.9977, 0.9984, 1);
--md-sys-motion-spatial-fast-duration: 360ms;
/* damping 0.8, stiffness 380; settles in 440ms, peaks at 1.015 */
--md-sys-motion-spatial-default: linear(0, 0.0145, 0.0527, 0.1076, 0.1736, 0.2461, 0.3214, 0.3968, 0.4702, 0.54, 0.6051, 0.665, 0.7193, 0.7679, 0.8108, 0.8483, 0.8807, 0.9083, 0.9316, 0.951, 0.9669, 0.9798, 0.99, 0.998, 1.004, 1.0085, 1.0116, 1.0136, 1.0147, 1.0151, 1.0151, 1.0146, 1.0138, 1.0128, 1.0118, 1.0106, 1.0095, 1.0084, 1.0073, 1.0063, 1.0053, 1.0045, 1.0037, 1.0031, 1.0025, 1.002, 1.0015, 1.0011, 1);
--md-sys-motion-spatial-default-duration: 440ms;
/* damping 0.8, stiffness 200; settles in 600ms, peaks at 1.015 */
--md-sys-motion-spatial-slow: linear(0, 0.0142, 0.0517, 0.1057, 0.1706, 0.2421, 0.3166, 0.3912, 0.464, 0.5334, 0.5984, 0.6583, 0.7127, 0.7615, 0.8047, 0.8426, 0.8755, 0.9036, 0.9274, 0.9473, 0.9638, 0.9771, 0.9878, 0.9962, 1.0027, 1.0074, 1.0108, 1.0131, 1.0144, 1.0151, 1.0151, 1.0148, 1.0141, 1.0132, 1.0122, 1.0111, 1.0099, 1.0088, 1.0077, 1.0067, 1.0057, 1.0049, 1.0041, 1.0034, 1.0027, 1.0022, 1.0017, 1.0013, 1);
--md-sys-motion-spatial-slow-duration: 600ms;
/* damping 1.0, stiffness 3800; settles in 150ms */
--md-sys-motion-effects-fast: linear(0, 0.0163, 0.0576, 0.1147, 0.1807, 0.2507, 0.3214, 0.3902, 0.4558, 0.5172, 0.5737, 0.6253, 0.6718, 0.7136, 0.7508, 0.7837, 0.8128, 0.8383, 0.8606, 0.8801, 0.897, 0.9117, 0.9244, 0.9353, 0.9448, 0.9529, 0.9599, 0.9658, 0.9709, 0.9753, 0.979, 0.9822, 0.9849, 0.9872, 0.9892, 0.9909, 0.9923, 0.9935, 0.9945, 0.9954, 0.9961, 0.9967, 0.9972, 0.9977, 0.998, 0.9983, 0.9986, 0.9988, 1);
--md-sys-motion-effects-fast-duration: 150ms;
/* damping 1.0, stiffness 1600; settles in 240ms */
--md-sys-motion-effects-default: linear(0, 0.0175, 0.0616, 0.1219, 0.1912, 0.2642, 0.3374, 0.4082, 0.4751, 0.5372, 0.594, 0.6454, 0.6916, 0.7326, 0.7689, 0.8009, 0.8288, 0.8532, 0.8743, 0.8926, 0.9084, 0.922, 0.9337, 0.9437, 0.9523, 0.9596, 0.9658, 0.9711, 0.9756, 0.9794, 0.9826, 0.9854, 0.9877, 0.9897, 0.9913, 0.9927, 0.9939, 0.9949, 0.9957, 0.9964, 0.997, 0.9975, 0.9979, 0.9982, 0.9985, 0.9988, 0.999, 0.9991, 1);
--md-sys-motion-effects-default-duration: 240ms;
/* damping 1.0, stiffness 800; settles in 330ms */
--md-sys-motion-effects-slow: linear(0, 0.0166, 0.0586, 0.1165, 0.1833, 0.254, 0.3253, 0.3947, 0.4606, 0.5221, 0.5788, 0.6303, 0.6768, 0.7184, 0.7554, 0.7881, 0.8169, 0.8421, 0.8641, 0.8833, 0.8999, 0.9144, 0.9268, 0.9375, 0.9467, 0.9546, 0.9614, 0.9672, 0.9722, 0.9764, 0.98, 0.9831, 0.9857, 0.9879, 0.9898, 0.9914, 0.9927, 0.9939, 0.9948, 0.9956, 0.9963, 0.9969, 0.9974, 0.9978, 0.9982, 0.9985, 0.9987, 0.9989, 1);
--md-sys-motion-effects-slow-duration: 330ms;
--md-sys-motion-easing-standard: cubic-bezier(0.2, 0, 0, 1);
--md-sys-motion-easing-standard-accelerate: cubic-bezier(0.3, 0, 1, 1);
--md-sys-motion-easing-standard-decelerate: cubic-bezier(0, 0, 0, 1);
--md-sys-motion-easing-emphasized: cubic-bezier(0.2, 0, 0, 1);
--md-sys-motion-easing-emphasized-accelerate: cubic-bezier(0.3, 0, 0.8, 0.15);
--md-sys-motion-easing-emphasized-decelerate: cubic-bezier(0.05, 0.7, 0.1, 1);
--md-sys-motion-duration-short: 150ms;
--md-sys-motion-duration-medium: 300ms;
--md-sys-motion-duration-long: 500ms;
}
@media (prefers-reduced-motion: reduce) {
:root {
--md-sys-motion-spatial-fast-duration: 0ms;
--md-sys-motion-spatial-default-duration: 0ms;
--md-sys-motion-spatial-slow-duration: 0ms;
--md-sys-motion-effects-fast-duration: 0ms;
--md-sys-motion-effects-default-duration: 0ms;
--md-sys-motion-effects-slow-duration: 0ms;
--md-sys-motion-duration-short: 0ms;
--md-sys-motion-duration-medium: 0ms;
--md-sys-motion-duration-long: 0ms;
}
}
+150
View File
@@ -0,0 +1,150 @@
/*
* Material 3 colour roles, generated by Google's material-color-utilities (spec 2025).
*
* php artisan material:scheme "#6750a4" --variant=tonal-spot
*
* Regenerate rather than editing a value: every pair here (a role and its on-role) carries
* M3's contrast guarantee only as generated. The head script sets data-theme before the
* first paint; the light block also stands without it.
*/
:root,
[data-theme='light'] {
color-scheme: light;
--md-sys-color-background: #fdf7fe;
--md-sys-color-on-background: #34313a;
--md-sys-color-surface: #fdf7fe;
--md-sys-color-surface-dim: #ded8e4;
--md-sys-color-surface-bright: #fdf7fe;
--md-sys-color-surface-container-lowest: #ffffff;
--md-sys-color-surface-container-low: #f8f1fa;
--md-sys-color-surface-container: #f2ecf5;
--md-sys-color-surface-container-high: #ece6f0;
--md-sys-color-surface-container-highest: #e7e0ec;
--md-sys-color-on-surface: #34313a;
--md-sys-color-on-surface-variant: #615d68;
--md-sys-color-outline: #7d7983;
--md-sys-color-outline-variant: #b5b0bb;
--md-sys-color-inverse-surface: #0f0d12;
--md-sys-color-inverse-on-surface: #a09ba1;
--md-sys-color-primary: #655789;
--md-sys-color-primary-dim: #594b7c;
--md-sys-color-on-primary: #fdf7ff;
--md-sys-color-primary-container: #d4c3fd;
--md-sys-color-on-primary-container: #493c6c;
--md-sys-color-primary-fixed: #d4c3fd;
--md-sys-color-primary-fixed-dim: #c6b6ee;
--md-sys-color-on-primary-fixed: #352857;
--md-sys-color-on-primary-fixed-variant: #524576;
--md-sys-color-inverse-primary: #d4c3fd;
--md-sys-color-secondary: #625c71;
--md-sys-color-secondary-dim: #565065;
--md-sys-color-on-secondary: #fdf7ff;
--md-sys-color-secondary-container: #e8def8;
--md-sys-color-on-secondary-container: #554f63;
--md-sys-color-secondary-fixed: #e8def8;
--md-sys-color-secondary-fixed-dim: #dad0ea;
--md-sys-color-on-secondary-fixed: #423c50;
--md-sys-color-on-secondary-fixed-variant: #5f586e;
--md-sys-color-tertiary: #7b5270;
--md-sys-color-tertiary-dim: #6e4664;
--md-sys-color-on-tertiary: #fff7f9;
--md-sys-color-tertiary-container: #f4bfe3;
--md-sys-color-on-tertiary-container: #5f3956;
--md-sys-color-tertiary-fixed: #f4bfe3;
--md-sys-color-tertiary-fixed-dim: #e5b2d5;
--md-sys-color-on-tertiary-fixed: #4a2642;
--md-sys-color-on-tertiary-fixed-variant: #694260;
--md-sys-color-error: #a8364b;
--md-sys-color-error-dim: #6b0221;
--md-sys-color-on-error: #fff7f7;
--md-sys-color-error-container: #f97386;
--md-sys-color-on-error-container: #6e0523;
--md-sys-color-success: #006c45;
--md-sys-color-on-success: #ffffff;
--md-sys-color-success-container: #86f9bc;
--md-sys-color-on-success-container: #002112;
--md-sys-color-warning: #7c5800;
--md-sys-color-on-warning: #ffffff;
--md-sys-color-warning-container: #ffdea6;
--md-sys-color-on-warning-container: #271900;
--md-sys-color-info: #005ac4;
--md-sys-color-on-info: #ffffff;
--md-sys-color-info-container: #d8e2ff;
--md-sys-color-on-info-container: #001a42;
--md-sys-color-inverse-error: #f97386;
--md-sys-color-inverse-success: #69dca1;
--md-sys-color-inverse-warning: #fdbb28;
--md-sys-color-inverse-info: #aec6ff;
}
[data-theme='dark'] {
color-scheme: dark;
--md-sys-color-background: #0f0d12;
--md-sys-color-on-background: #eae3ef;
--md-sys-color-surface: #0f0d12;
--md-sys-color-surface-dim: #0f0d12;
--md-sys-color-surface-bright: #2e2b34;
--md-sys-color-surface-container-lowest: #000000;
--md-sys-color-surface-container-low: #141218;
--md-sys-color-surface-container: #1b181f;
--md-sys-color-surface-container-high: #211e26;
--md-sys-color-surface-container-highest: #27242d;
--md-sys-color-on-surface: #eae3ef;
--md-sys-color-on-surface-variant: #aea9b4;
--md-sys-color-outline: #78737e;
--md-sys-color-outline-variant: #4a4650;
--md-sys-color-inverse-surface: #fdf7fe;
--md-sys-color-inverse-on-surface: #575459;
--md-sys-color-primary: #cdc0ec;
--md-sys-color-primary-dim: #bfb2de;
--md-sys-color-on-primary: #443a5f;
--md-sys-color-primary-container: #574d72;
--md-sys-color-on-primary-container: #e9deff;
--md-sys-color-primary-fixed: #ded0fe;
--md-sys-color-primary-fixed-dim: #d0c3ef;
--md-sys-color-on-primary-fixed: #3c3256;
--md-sys-color-on-primary-fixed-variant: #594e74;
--md-sys-color-inverse-primary: #645980;
--md-sys-color-secondary: #cbc2db;
--md-sys-color-secondary-dim: #beb5cd;
--md-sys-color-on-secondary: #433d51;
--md-sys-color-secondary-container: #3e384c;
--md-sys-color-on-secondary-container: #c4bbd4;
--md-sys-color-secondary-fixed: #e8def8;
--md-sys-color-secondary-fixed-dim: #dad0ea;
--md-sys-color-on-secondary-fixed: #423c50;
--md-sys-color-on-secondary-fixed-variant: #5f586e;
--md-sys-color-tertiary: #ffcfef;
--md-sys-color-tertiary-dim: #f4bfe3;
--md-sys-color-on-tertiary: #69415f;
--md-sys-color-tertiary-container: #f4bfe3;
--md-sys-color-on-tertiary-container: #5f3956;
--md-sys-color-tertiary-fixed: #f4bfe3;
--md-sys-color-tertiary-fixed-dim: #e5b2d5;
--md-sys-color-on-tertiary-fixed: #4a2642;
--md-sys-color-on-tertiary-fixed-variant: #694260;
--md-sys-color-error: #f97386;
--md-sys-color-error-dim: #c44b5f;
--md-sys-color-on-error: #490013;
--md-sys-color-error-container: #871c34;
--md-sys-color-on-error-container: #ff97a3;
--md-sys-color-success: #69dca1;
--md-sys-color-on-success: #003822;
--md-sys-color-success-container: #005233;
--md-sys-color-on-success-container: #86f9bc;
--md-sys-color-warning: #fdbb28;
--md-sys-color-on-warning: #412d00;
--md-sys-color-warning-container: #5e4200;
--md-sys-color-on-warning-container: #ffdea6;
--md-sys-color-info: #aec6ff;
--md-sys-color-on-info: #002e6a;
--md-sys-color-info-container: #004396;
--md-sys-color-on-info-container: #d8e2ff;
--md-sys-color-inverse-error: #a8364b;
--md-sys-color-inverse-success: #006c45;
--md-sys-color-inverse-warning: #7c5800;
--md-sys-color-inverse-info: #005ac4;
}
+140
View File
@@ -0,0 +1,140 @@
{
"seed": "#6750a4",
"variant": "tonal-spot",
"spec": "2025",
"contrast": 0,
"light": {
"background": "#fdf7fe",
"on-background": "#34313a",
"surface": "#fdf7fe",
"surface-dim": "#ded8e4",
"surface-bright": "#fdf7fe",
"surface-container-lowest": "#ffffff",
"surface-container-low": "#f8f1fa",
"surface-container": "#f2ecf5",
"surface-container-high": "#ece6f0",
"surface-container-highest": "#e7e0ec",
"on-surface": "#34313a",
"on-surface-variant": "#615d68",
"outline": "#7d7983",
"outline-variant": "#b5b0bb",
"inverse-surface": "#0f0d12",
"inverse-on-surface": "#a09ba1",
"primary": "#655789",
"primary-dim": "#594b7c",
"on-primary": "#fdf7ff",
"primary-container": "#d4c3fd",
"on-primary-container": "#493c6c",
"primary-fixed": "#d4c3fd",
"primary-fixed-dim": "#c6b6ee",
"on-primary-fixed": "#352857",
"on-primary-fixed-variant": "#524576",
"inverse-primary": "#d4c3fd",
"secondary": "#625c71",
"secondary-dim": "#565065",
"on-secondary": "#fdf7ff",
"secondary-container": "#e8def8",
"on-secondary-container": "#554f63",
"secondary-fixed": "#e8def8",
"secondary-fixed-dim": "#dad0ea",
"on-secondary-fixed": "#423c50",
"on-secondary-fixed-variant": "#5f586e",
"tertiary": "#7b5270",
"tertiary-dim": "#6e4664",
"on-tertiary": "#fff7f9",
"tertiary-container": "#f4bfe3",
"on-tertiary-container": "#5f3956",
"tertiary-fixed": "#f4bfe3",
"tertiary-fixed-dim": "#e5b2d5",
"on-tertiary-fixed": "#4a2642",
"on-tertiary-fixed-variant": "#694260",
"error": "#a8364b",
"error-dim": "#6b0221",
"on-error": "#fff7f7",
"error-container": "#f97386",
"on-error-container": "#6e0523",
"success": "#006c45",
"on-success": "#ffffff",
"success-container": "#86f9bc",
"on-success-container": "#002112",
"warning": "#7c5800",
"on-warning": "#ffffff",
"warning-container": "#ffdea6",
"on-warning-container": "#271900",
"info": "#005ac4",
"on-info": "#ffffff",
"info-container": "#d8e2ff",
"on-info-container": "#001a42",
"inverse-error": "#f97386",
"inverse-success": "#69dca1",
"inverse-warning": "#fdbb28",
"inverse-info": "#aec6ff"
},
"dark": {
"background": "#0f0d12",
"on-background": "#eae3ef",
"surface": "#0f0d12",
"surface-dim": "#0f0d12",
"surface-bright": "#2e2b34",
"surface-container-lowest": "#000000",
"surface-container-low": "#141218",
"surface-container": "#1b181f",
"surface-container-high": "#211e26",
"surface-container-highest": "#27242d",
"on-surface": "#eae3ef",
"on-surface-variant": "#aea9b4",
"outline": "#78737e",
"outline-variant": "#4a4650",
"inverse-surface": "#fdf7fe",
"inverse-on-surface": "#575459",
"primary": "#cdc0ec",
"primary-dim": "#bfb2de",
"on-primary": "#443a5f",
"primary-container": "#574d72",
"on-primary-container": "#e9deff",
"primary-fixed": "#ded0fe",
"primary-fixed-dim": "#d0c3ef",
"on-primary-fixed": "#3c3256",
"on-primary-fixed-variant": "#594e74",
"inverse-primary": "#645980",
"secondary": "#cbc2db",
"secondary-dim": "#beb5cd",
"on-secondary": "#433d51",
"secondary-container": "#3e384c",
"on-secondary-container": "#c4bbd4",
"secondary-fixed": "#e8def8",
"secondary-fixed-dim": "#dad0ea",
"on-secondary-fixed": "#423c50",
"on-secondary-fixed-variant": "#5f586e",
"tertiary": "#ffcfef",
"tertiary-dim": "#f4bfe3",
"on-tertiary": "#69415f",
"tertiary-container": "#f4bfe3",
"on-tertiary-container": "#5f3956",
"tertiary-fixed": "#f4bfe3",
"tertiary-fixed-dim": "#e5b2d5",
"on-tertiary-fixed": "#4a2642",
"on-tertiary-fixed-variant": "#694260",
"error": "#f97386",
"error-dim": "#c44b5f",
"on-error": "#490013",
"error-container": "#871c34",
"on-error-container": "#ff97a3",
"success": "#69dca1",
"on-success": "#003822",
"success-container": "#005233",
"on-success-container": "#86f9bc",
"warning": "#fdbb28",
"on-warning": "#412d00",
"warning-container": "#5e4200",
"on-warning-container": "#ffdea6",
"info": "#aec6ff",
"on-info": "#002e6a",
"info-container": "#004396",
"on-info-container": "#d8e2ff",
"inverse-error": "#a8364b",
"inverse-success": "#006c45",
"inverse-warning": "#7c5800",
"inverse-info": "#005ac4"
}
}
+21
View File
@@ -0,0 +1,21 @@
/*
* Shape, as M3's corner scale.
*
* Values from @material/web's token files (Apache-2.0, © Google LLC). `full` is a large
* length rather than @material/web's `50cqmin`, which resolves against a container an
* application may never declare.
*
* As utilities: rounded-corner-xs … rounded-corner-full.
*/
:root {
--md-sys-shape-corner-none: 0px;
--md-sys-shape-corner-xs: 4px;
--md-sys-shape-corner-sm: 8px;
--md-sys-shape-corner-md: 12px;
--md-sys-shape-corner-lg: 16px;
--md-sys-shape-corner-lg-increased: 20px;
--md-sys-shape-corner-xl: 28px;
--md-sys-shape-corner-xl-increased: 32px;
--md-sys-shape-corner-xxl: 48px;
--md-sys-shape-corner-full: 9999px;
}
+70
View File
@@ -0,0 +1,70 @@
/*
* Interaction states, as M3 draws them.
*
* `state-layer` puts M3's state layer on an element: its content colour laid over it at 8%
* on hover, 10% on focus and press. Hover only where the pointer can hover, because a touch
* screen keeps the last hover after a tap. The layer is a ::before, so the element becomes
* `position: relative` and `isolation: isolate`; never pass `static` or a z-index to it.
*
* `focus-ring` is M3's focus indicator — 3px in secondary, 2px out — drawn only for keyboard
* focus. `link` is a link in running text: underlined in the colour of the words around it.
*/
@utility state-layer {
position: relative;
isolation: isolate;
&::before {
content: '';
position: absolute;
inset: 0;
z-index: -1;
border-radius: inherit;
background-color: currentColor;
opacity: 0;
pointer-events: none;
transition: opacity var(--md-sys-motion-effects-fast-duration) var(--md-sys-motion-effects-fast);
}
@media (hover: hover) {
&:hover::before {
opacity: 0.08;
}
}
&:focus-visible::before,
&:active::before {
opacity: 0.1;
}
&:disabled::before,
&[aria-disabled='true']::before {
display: none;
}
}
@utility focus-ring {
outline: none;
&:focus-visible {
outline: 3px solid var(--md-sys-color-secondary);
outline-offset: 2px;
}
}
@utility link {
cursor: pointer;
text-decoration-line: underline;
text-decoration-thickness: from-font;
text-underline-offset: 0.15em;
border-radius: var(--md-sys-shape-corner-xs);
&:focus-visible {
outline: 3px solid var(--md-sys-color-secondary);
outline-offset: 2px;
}
}
[x-cloak] {
display: none !important;
}
+143
View File
@@ -0,0 +1,143 @@
/*
* The Tailwind theme: every token as a utility.
*
* `--color-*: initial` clears Tailwind's default palette, so every colour a template can name
* is an M3 role (bg-primary, text-on-surface-variant, border-outline-variant…) and a
* `text-red-600` compiles to nothing. White and black come back as the two absolutes. An
* application that wants more colours adds them in its own @theme after importing this file.
*
* The colour blocks are `@theme inline`: `bg-primary` compiles to
* `background-color: var(--md-sys-color-primary)`, resolved on the element itself. Without
* `inline` it compiles to `var(--color-primary)`, a variable computed once on :root — the
* page-wide theme still switches, but a `data-theme="dark"` section inside a light page keeps
* the light colours. Only blocks whose values are variables may be inline; a literal value in
* an inline block would be copied into every utility and lose its variable.
*/
@theme {
--color-*: initial;
--color-white: #ffffff;
--color-black: #000000;
--font-sans: var(--md-ref-typeface-brand);
}
/* The colour roles, as generated by `php artisan material:scheme`. */
@theme inline {
--color-background: var(--md-sys-color-background);
--color-on-background: var(--md-sys-color-on-background);
--color-surface: var(--md-sys-color-surface);
--color-surface-dim: var(--md-sys-color-surface-dim);
--color-surface-bright: var(--md-sys-color-surface-bright);
--color-surface-container-lowest: var(--md-sys-color-surface-container-lowest);
--color-surface-container-low: var(--md-sys-color-surface-container-low);
--color-surface-container: var(--md-sys-color-surface-container);
--color-surface-container-high: var(--md-sys-color-surface-container-high);
--color-surface-container-highest: var(--md-sys-color-surface-container-highest);
--color-on-surface: var(--md-sys-color-on-surface);
--color-on-surface-variant: var(--md-sys-color-on-surface-variant);
--color-outline: var(--md-sys-color-outline);
--color-outline-variant: var(--md-sys-color-outline-variant);
--color-inverse-surface: var(--md-sys-color-inverse-surface);
--color-inverse-on-surface: var(--md-sys-color-inverse-on-surface);
--color-primary: var(--md-sys-color-primary);
--color-primary-dim: var(--md-sys-color-primary-dim);
--color-on-primary: var(--md-sys-color-on-primary);
--color-primary-container: var(--md-sys-color-primary-container);
--color-on-primary-container: var(--md-sys-color-on-primary-container);
--color-primary-fixed: var(--md-sys-color-primary-fixed);
--color-primary-fixed-dim: var(--md-sys-color-primary-fixed-dim);
--color-on-primary-fixed: var(--md-sys-color-on-primary-fixed);
--color-on-primary-fixed-variant: var(--md-sys-color-on-primary-fixed-variant);
--color-inverse-primary: var(--md-sys-color-inverse-primary);
--color-secondary: var(--md-sys-color-secondary);
--color-secondary-dim: var(--md-sys-color-secondary-dim);
--color-on-secondary: var(--md-sys-color-on-secondary);
--color-secondary-container: var(--md-sys-color-secondary-container);
--color-on-secondary-container: var(--md-sys-color-on-secondary-container);
--color-secondary-fixed: var(--md-sys-color-secondary-fixed);
--color-secondary-fixed-dim: var(--md-sys-color-secondary-fixed-dim);
--color-on-secondary-fixed: var(--md-sys-color-on-secondary-fixed);
--color-on-secondary-fixed-variant: var(--md-sys-color-on-secondary-fixed-variant);
--color-tertiary: var(--md-sys-color-tertiary);
--color-tertiary-dim: var(--md-sys-color-tertiary-dim);
--color-on-tertiary: var(--md-sys-color-on-tertiary);
--color-tertiary-container: var(--md-sys-color-tertiary-container);
--color-on-tertiary-container: var(--md-sys-color-on-tertiary-container);
--color-tertiary-fixed: var(--md-sys-color-tertiary-fixed);
--color-tertiary-fixed-dim: var(--md-sys-color-tertiary-fixed-dim);
--color-on-tertiary-fixed: var(--md-sys-color-on-tertiary-fixed);
--color-on-tertiary-fixed-variant: var(--md-sys-color-on-tertiary-fixed-variant);
--color-error: var(--md-sys-color-error);
--color-error-dim: var(--md-sys-color-error-dim);
--color-on-error: var(--md-sys-color-on-error);
--color-error-container: var(--md-sys-color-error-container);
--color-on-error-container: var(--md-sys-color-on-error-container);
--color-success: var(--md-sys-color-success);
--color-on-success: var(--md-sys-color-on-success);
--color-success-container: var(--md-sys-color-success-container);
--color-on-success-container: var(--md-sys-color-on-success-container);
--color-warning: var(--md-sys-color-warning);
--color-on-warning: var(--md-sys-color-on-warning);
--color-warning-container: var(--md-sys-color-warning-container);
--color-on-warning-container: var(--md-sys-color-on-warning-container);
--color-info: var(--md-sys-color-info);
--color-on-info: var(--md-sys-color-on-info);
--color-info-container: var(--md-sys-color-info-container);
--color-on-info-container: var(--md-sys-color-on-info-container);
--color-inverse-error: var(--md-sys-color-inverse-error);
--color-inverse-success: var(--md-sys-color-inverse-success);
--color-inverse-warning: var(--md-sys-color-inverse-warning);
--color-inverse-info: var(--md-sys-color-inverse-info);
--color-shadow: var(--md-sys-color-shadow);
--color-scrim: var(--md-sys-color-scrim);
}
/*
* Ink and lines by what they are, rather than by an opacity number that means nothing out of
* context: body copy, metadata, decoration; a structural line, a line around chrome, a divider.
*/
@theme inline {
--color-body: var(--md-sys-color-on-surface-variant);
--color-meta: color-mix(in oklab, var(--md-sys-color-on-surface) 60%, transparent);
--color-quiet: color-mix(in oklab, var(--md-sys-color-on-surface) 38%, transparent);
--color-structure: var(--md-sys-color-outline-variant);
--color-chrome: color-mix(in oklab, var(--md-sys-color-outline-variant) 60%, transparent);
--color-divider: color-mix(in oklab, var(--md-sys-color-outline-variant) 40%, transparent);
}
@theme {
--radius-corner-none: var(--md-sys-shape-corner-none);
--radius-corner-xs: var(--md-sys-shape-corner-xs);
--radius-corner-sm: var(--md-sys-shape-corner-sm);
--radius-corner-md: var(--md-sys-shape-corner-md);
--radius-corner-lg: var(--md-sys-shape-corner-lg);
--radius-corner-lg-increased: var(--md-sys-shape-corner-lg-increased);
--radius-corner-xl: var(--md-sys-shape-corner-xl);
--radius-corner-xl-increased: var(--md-sys-shape-corner-xl-increased);
--radius-corner-xxl: var(--md-sys-shape-corner-xxl);
--radius-corner-full: var(--md-sys-shape-corner-full);
--shadow-elevation-1: var(--md-sys-elevation-1);
--shadow-elevation-2: var(--md-sys-elevation-2);
--shadow-elevation-3: var(--md-sys-elevation-3);
--shadow-elevation-4: var(--md-sys-elevation-4);
--shadow-elevation-5: var(--md-sys-elevation-5);
--ease-spatial-fast: var(--md-sys-motion-spatial-fast);
--ease-spatial-default: var(--md-sys-motion-spatial-default);
--ease-spatial-slow: var(--md-sys-motion-spatial-slow);
--ease-effects-fast: var(--md-sys-motion-effects-fast);
--ease-effects-default: var(--md-sys-motion-effects-default);
--ease-effects-slow: var(--md-sys-motion-effects-slow);
--ease-standard: var(--md-sys-motion-easing-standard);
--ease-emphasized: var(--md-sys-motion-easing-emphasized);
--ease-emphasized-decelerate: var(--md-sys-motion-easing-emphasized-decelerate);
--ease-emphasized-accelerate: var(--md-sys-motion-easing-emphasized-accelerate);
}
/*
* `dark:` follows the page's theme, not the operating system: <x-theme-script> has already
* resolved `system` into data-theme, and the OS was never meant to be a second voice.
*/
@custom-variant dark (&:where([data-theme='dark'], [data-theme='dark'] *));
+231
View File
@@ -0,0 +1,231 @@
/*
* Type, as M3's typescale.
*
* Sizes, line heights and tracking are @material/web's token values (Apache-2.0, © Google
* LLC). A style is used through its utility, never by hand-assembling `text-*`, `leading-*`
* and `tracking-*`:
*
* <h2 class="type-title-lg">…</h2>
* <p class="type-emphasized-headline-md tabular-nums">…</p>
*
* The brand typeface is Google Sans Flex (font.css). Emphasized styles are one weight step
* heavier and fully rounded ("ROND" 100) — the axis that typeface exists for.
*/
:root {
--md-ref-typeface-brand: 'Google Sans Flex', ui-sans-serif, system-ui, sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol', 'Noto Color Emoji';
--md-ref-typeface-plain: var(--md-ref-typeface-brand);
--md-ref-typeface-weight-regular: 400;
--md-ref-typeface-weight-medium: 500;
--md-ref-typeface-weight-bold: 700;
--md-sys-typescale-display-lg: var(--md-ref-typeface-weight-regular) 3.5625rem/4rem var(--md-ref-typeface-brand);
--md-sys-typescale-display-lg-tracking: -0.015625rem;
--md-sys-typescale-emphasized-display-lg: var(--md-ref-typeface-weight-medium) 3.5625rem/4rem var(--md-ref-typeface-brand);
--md-sys-typescale-display-md: var(--md-ref-typeface-weight-regular) 2.8125rem/3.25rem var(--md-ref-typeface-brand);
--md-sys-typescale-display-md-tracking: 0rem;
--md-sys-typescale-emphasized-display-md: var(--md-ref-typeface-weight-medium) 2.8125rem/3.25rem var(--md-ref-typeface-brand);
--md-sys-typescale-display-sm: var(--md-ref-typeface-weight-regular) 2.25rem/2.75rem var(--md-ref-typeface-brand);
--md-sys-typescale-display-sm-tracking: 0rem;
--md-sys-typescale-emphasized-display-sm: var(--md-ref-typeface-weight-medium) 2.25rem/2.75rem var(--md-ref-typeface-brand);
--md-sys-typescale-headline-lg: var(--md-ref-typeface-weight-regular) 2rem/2.5rem var(--md-ref-typeface-brand);
--md-sys-typescale-headline-lg-tracking: 0rem;
--md-sys-typescale-emphasized-headline-lg: var(--md-ref-typeface-weight-medium) 2rem/2.5rem var(--md-ref-typeface-brand);
--md-sys-typescale-headline-md: var(--md-ref-typeface-weight-regular) 1.75rem/2.25rem var(--md-ref-typeface-brand);
--md-sys-typescale-headline-md-tracking: 0rem;
--md-sys-typescale-emphasized-headline-md: var(--md-ref-typeface-weight-medium) 1.75rem/2.25rem var(--md-ref-typeface-brand);
--md-sys-typescale-headline-sm: var(--md-ref-typeface-weight-regular) 1.5rem/2rem var(--md-ref-typeface-brand);
--md-sys-typescale-headline-sm-tracking: 0rem;
--md-sys-typescale-emphasized-headline-sm: var(--md-ref-typeface-weight-medium) 1.5rem/2rem var(--md-ref-typeface-brand);
--md-sys-typescale-title-lg: var(--md-ref-typeface-weight-regular) 1.375rem/1.75rem var(--md-ref-typeface-brand);
--md-sys-typescale-title-lg-tracking: 0rem;
--md-sys-typescale-emphasized-title-lg: var(--md-ref-typeface-weight-medium) 1.375rem/1.75rem var(--md-ref-typeface-brand);
--md-sys-typescale-title-md: var(--md-ref-typeface-weight-medium) 1rem/1.5rem var(--md-ref-typeface-plain);
--md-sys-typescale-title-md-tracking: 0.009375rem;
--md-sys-typescale-emphasized-title-md: var(--md-ref-typeface-weight-bold) 1rem/1.5rem var(--md-ref-typeface-plain);
--md-sys-typescale-title-sm: var(--md-ref-typeface-weight-medium) 0.875rem/1.25rem var(--md-ref-typeface-plain);
--md-sys-typescale-title-sm-tracking: 0.00625rem;
--md-sys-typescale-emphasized-title-sm: var(--md-ref-typeface-weight-bold) 0.875rem/1.25rem var(--md-ref-typeface-plain);
--md-sys-typescale-body-lg: var(--md-ref-typeface-weight-regular) 1rem/1.5rem var(--md-ref-typeface-plain);
--md-sys-typescale-body-lg-tracking: 0.03125rem;
--md-sys-typescale-emphasized-body-lg: var(--md-ref-typeface-weight-medium) 1rem/1.5rem var(--md-ref-typeface-plain);
--md-sys-typescale-body-md: var(--md-ref-typeface-weight-regular) 0.875rem/1.25rem var(--md-ref-typeface-plain);
--md-sys-typescale-body-md-tracking: 0.015625rem;
--md-sys-typescale-emphasized-body-md: var(--md-ref-typeface-weight-medium) 0.875rem/1.25rem var(--md-ref-typeface-plain);
--md-sys-typescale-body-sm: var(--md-ref-typeface-weight-regular) 0.75rem/1rem var(--md-ref-typeface-plain);
--md-sys-typescale-body-sm-tracking: 0.025rem;
--md-sys-typescale-emphasized-body-sm: var(--md-ref-typeface-weight-medium) 0.75rem/1rem var(--md-ref-typeface-plain);
--md-sys-typescale-label-lg: var(--md-ref-typeface-weight-medium) 0.875rem/1.25rem var(--md-ref-typeface-plain);
--md-sys-typescale-label-lg-tracking: 0.00625rem;
--md-sys-typescale-emphasized-label-lg: var(--md-ref-typeface-weight-bold) 0.875rem/1.25rem var(--md-ref-typeface-plain);
--md-sys-typescale-label-md: var(--md-ref-typeface-weight-medium) 0.75rem/1rem var(--md-ref-typeface-plain);
--md-sys-typescale-label-md-tracking: 0.03125rem;
--md-sys-typescale-emphasized-label-md: var(--md-ref-typeface-weight-bold) 0.75rem/1rem var(--md-ref-typeface-plain);
--md-sys-typescale-label-sm: var(--md-ref-typeface-weight-medium) 0.6875rem/1rem var(--md-ref-typeface-plain);
--md-sys-typescale-label-sm-tracking: 0.03125rem;
--md-sys-typescale-emphasized-label-sm: var(--md-ref-typeface-weight-bold) 0.6875rem/1rem var(--md-ref-typeface-plain);
}
@utility type-display-lg {
font: var(--md-sys-typescale-display-lg);
letter-spacing: var(--md-sys-typescale-display-lg-tracking);
}
@utility type-emphasized-display-lg {
font: var(--md-sys-typescale-emphasized-display-lg);
letter-spacing: var(--md-sys-typescale-display-lg-tracking);
font-variation-settings: "ROND" 100;
}
@utility type-display-md {
font: var(--md-sys-typescale-display-md);
letter-spacing: var(--md-sys-typescale-display-md-tracking);
}
@utility type-emphasized-display-md {
font: var(--md-sys-typescale-emphasized-display-md);
letter-spacing: var(--md-sys-typescale-display-md-tracking);
font-variation-settings: "ROND" 100;
}
@utility type-display-sm {
font: var(--md-sys-typescale-display-sm);
letter-spacing: var(--md-sys-typescale-display-sm-tracking);
}
@utility type-emphasized-display-sm {
font: var(--md-sys-typescale-emphasized-display-sm);
letter-spacing: var(--md-sys-typescale-display-sm-tracking);
font-variation-settings: "ROND" 100;
}
@utility type-headline-lg {
font: var(--md-sys-typescale-headline-lg);
letter-spacing: var(--md-sys-typescale-headline-lg-tracking);
}
@utility type-emphasized-headline-lg {
font: var(--md-sys-typescale-emphasized-headline-lg);
letter-spacing: var(--md-sys-typescale-headline-lg-tracking);
font-variation-settings: "ROND" 100;
}
@utility type-headline-md {
font: var(--md-sys-typescale-headline-md);
letter-spacing: var(--md-sys-typescale-headline-md-tracking);
}
@utility type-emphasized-headline-md {
font: var(--md-sys-typescale-emphasized-headline-md);
letter-spacing: var(--md-sys-typescale-headline-md-tracking);
font-variation-settings: "ROND" 100;
}
@utility type-headline-sm {
font: var(--md-sys-typescale-headline-sm);
letter-spacing: var(--md-sys-typescale-headline-sm-tracking);
}
@utility type-emphasized-headline-sm {
font: var(--md-sys-typescale-emphasized-headline-sm);
letter-spacing: var(--md-sys-typescale-headline-sm-tracking);
font-variation-settings: "ROND" 100;
}
@utility type-title-lg {
font: var(--md-sys-typescale-title-lg);
letter-spacing: var(--md-sys-typescale-title-lg-tracking);
}
@utility type-emphasized-title-lg {
font: var(--md-sys-typescale-emphasized-title-lg);
letter-spacing: var(--md-sys-typescale-title-lg-tracking);
font-variation-settings: "ROND" 100;
}
@utility type-title-md {
font: var(--md-sys-typescale-title-md);
letter-spacing: var(--md-sys-typescale-title-md-tracking);
}
@utility type-emphasized-title-md {
font: var(--md-sys-typescale-emphasized-title-md);
letter-spacing: var(--md-sys-typescale-title-md-tracking);
font-variation-settings: "ROND" 100;
}
@utility type-title-sm {
font: var(--md-sys-typescale-title-sm);
letter-spacing: var(--md-sys-typescale-title-sm-tracking);
}
@utility type-emphasized-title-sm {
font: var(--md-sys-typescale-emphasized-title-sm);
letter-spacing: var(--md-sys-typescale-title-sm-tracking);
font-variation-settings: "ROND" 100;
}
@utility type-body-lg {
font: var(--md-sys-typescale-body-lg);
letter-spacing: var(--md-sys-typescale-body-lg-tracking);
}
@utility type-emphasized-body-lg {
font: var(--md-sys-typescale-emphasized-body-lg);
letter-spacing: var(--md-sys-typescale-body-lg-tracking);
font-variation-settings: "ROND" 100;
}
@utility type-body-md {
font: var(--md-sys-typescale-body-md);
letter-spacing: var(--md-sys-typescale-body-md-tracking);
}
@utility type-emphasized-body-md {
font: var(--md-sys-typescale-emphasized-body-md);
letter-spacing: var(--md-sys-typescale-body-md-tracking);
font-variation-settings: "ROND" 100;
}
@utility type-body-sm {
font: var(--md-sys-typescale-body-sm);
letter-spacing: var(--md-sys-typescale-body-sm-tracking);
}
@utility type-emphasized-body-sm {
font: var(--md-sys-typescale-emphasized-body-sm);
letter-spacing: var(--md-sys-typescale-body-sm-tracking);
font-variation-settings: "ROND" 100;
}
@utility type-label-lg {
font: var(--md-sys-typescale-label-lg);
letter-spacing: var(--md-sys-typescale-label-lg-tracking);
}
@utility type-emphasized-label-lg {
font: var(--md-sys-typescale-emphasized-label-lg);
letter-spacing: var(--md-sys-typescale-label-lg-tracking);
font-variation-settings: "ROND" 100;
}
@utility type-label-md {
font: var(--md-sys-typescale-label-md);
letter-spacing: var(--md-sys-typescale-label-md-tracking);
}
@utility type-emphasized-label-md {
font: var(--md-sys-typescale-emphasized-label-md);
letter-spacing: var(--md-sys-typescale-label-md-tracking);
font-variation-settings: "ROND" 100;
}
@utility type-label-sm {
font: var(--md-sys-typescale-label-sm);
letter-spacing: var(--md-sys-typescale-label-sm-tracking);
}
@utility type-emphasized-label-sm {
font: var(--md-sys-typescale-emphasized-label-sm);
letter-spacing: var(--md-sys-typescale-label-sm-tracking);
font-variation-settings: "ROND" 100;
}
+91
View File
@@ -0,0 +1,91 @@
This Font Software is licensed under the SIL Open Font License, Version 1.1.
This license is copied below, and is also available with a FAQ at:
https://openfontlicense.org
-----------------------------------------------------------
SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007
-----------------------------------------------------------
PREAMBLE
The goals of the Open Font License (OFL) are to stimulate worldwide
development of collaborative font projects, to support the font creation
efforts of academic and linguistic communities, and to provide a free and
open framework in which fonts may be shared and improved in partnership
with others.
The OFL allows the licensed fonts to be used, studied, modified and
redistributed freely as long as they are not sold by themselves. The
fonts, including any derivative works, can be bundled, embedded,
redistributed and/or sold with any software provided that any reserved
names are not used by derivative works. The fonts and derivatives,
however, cannot be released under any other type of license. The
requirement for fonts to remain under this license does not apply
to any document created using the fonts or their derivatives.
DEFINITIONS
"Font Software" refers to the set of files released by the Copyright
Holder(s) under this license and clearly marked as such. This may
include source files, build scripts and documentation.
"Reserved Font Name" refers to any names specified as such after the
copyright statement(s).
"Original Version" refers to the collection of Font Software components as
distributed by the Copyright Holder(s).
"Modified Version" refers to any derivative made by adding to, deleting,
or substituting -- in part or in whole -- any of the components of the
Original Version, by changing formats or by porting the Font Software to a
new environment.
"Author" refers to any designer, engineer, programmer, technical
writer or other person who contributed to the Font Software.
PERMISSION & CONDITIONS
Permission is hereby granted, free of charge, to any person obtaining
a copy of the Font Software, to use, study, copy, merge, embed, modify,
redistribute, and sell modified and unmodified copies of the Font
Software, subject to the following conditions:
1) Neither the Font Software nor any of its individual components,
in Original or Modified Versions, may be sold by itself.
2) Original or Modified Versions of the Font Software may be bundled,
redistributed and/or sold with any software, provided that each copy
contains the above copyright notice and this license. These can be
included either as stand-alone text files, human-readable headers or
in the appropriate machine-readable metadata fields within text or
binary files as long as those fields can be easily viewed by the user.
3) No Modified Version of the Font Software may use the Reserved Font
Name(s) unless explicit written permission is granted by the corresponding
Copyright Holder. This restriction only applies to the primary font name as
presented to the users.
4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font
Software shall not be used to promote, endorse or advertise any
Modified Version, except to acknowledge the contribution(s) of the
Copyright Holder(s) and the Author(s) or with their explicit written
permission.
5) The Font Software, modified or unmodified, in part or in whole,
must be distributed entirely under this license, and must not be
distributed under any other license. The requirement for fonts to
remain under this license does not apply to any document created
using the Font Software.
TERMINATION
This license becomes null and void if any of the above conditions are
not met.
DISCLAIMER
THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT
OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE
COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL
DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM
OTHER DEALINGS IN THE FONT SOFTWARE.
+121
View File
@@ -0,0 +1,121 @@
/**
* `x-figure`: a figure counts to its value — from zero when it first appears, and from what
* it said before when the server changes it.
*
* The server renders the real value, so without JavaScript, in a test or in a screenshot the
* figure is simply right. Only a figure with exactly one number in it moves — "39.6",
* "1,204 km", "CHF 12.50" — and a time, a date or a range is left alone rather than counted
* as if it were one quantity. The format is the target's: its decimals and its thousands
* separator, so a column of counting figures keeps its width under `tabular-nums`.
*
* The curve decelerates and never overshoots, unlike the spatial springs, because a figure
* that passes its value is briefly a lie. The duration is the spatial-slow token's, which
* reduced motion sets to zero — and then the figure is written once, as it is.
*
* A re-render that leaves the text the same never reaches the observer, so a Livewire round
* trip does not replay it; one that changes it counts from the figure on screen, even
* mid-count. The observer ignores the directive's own writes by comparing against the last
* thing it wrote.
*/
const FIGURE = /[-]?\d{1,3}(?:[,']\d{3})+(?:\.\d+)?|[-]?\d+(?:\.\d+)?/g
const readFigure = (text) => {
const matches = [...text.matchAll(FIGURE)]
if (matches.length !== 1) {
return null
}
const [raw] = matches[0]
return {
value: Number(raw.replace(/[,']/g, '').replace('', '-')),
decimals: raw.includes('.') ? raw.split('.')[1].length : 0,
separator: raw.match(/\d([,'])\d{3}/)?.[1] ?? '',
minus: raw.startsWith('') ? '' : '-',
before: text.slice(0, matches[0].index),
after: text.slice(matches[0].index + raw.length),
}
}
const writeFigure = (figure, value) => {
let [whole, fraction] = Math.abs(value).toFixed(figure.decimals).split('.')
if (figure.separator) {
whole = whole.replace(/\B(?=(\d{3})+(?!\d))/g, figure.separator)
}
const sign = value < 0 && Number(Math.abs(value).toFixed(figure.decimals)) !== 0 ? figure.minus : ''
return figure.before + sign + whole + (fraction ? `.${fraction}` : '') + figure.after
}
document.addEventListener('alpine:init', () => {
window.Alpine.directive('figure', (el, _, { cleanup }) => {
const text = () => (el.childNodes.length === 1 && el.firstChild.nodeType === Node.TEXT_NODE ? el.firstChild : null)
// In milliseconds, whichever unit arrives: a minifier turns `600ms` into `.6s`, and a
// bare parseFloat would read that as 0.6ms — an instant count.
const duration = () => {
const token = getComputedStyle(document.documentElement).getPropertyValue('--md-sys-motion-spatial-slow-duration').trim()
const value = parseFloat(token) || 0
return token.endsWith('ms') ? value : value * 1000
}
let written = text()?.data ?? null
let frame = null
const count = (from, target) => {
cancelAnimationFrame(frame)
const node = text()
const length = duration()
if (!node || !target || length === 0 || from === target.value) {
return
}
const started = performance.now()
const step = (now) => {
const progress = Math.min(1, (now - started) / length)
const eased = 1 - Math.pow(1 - progress, 4)
written = progress === 1 ? writeFigure(target, target.value) : writeFigure(target, from + (target.value - from) * eased)
node.data = written
if (progress < 1) {
frame = requestAnimationFrame(step)
}
}
written = writeFigure(target, from)
node.data = written
frame = requestAnimationFrame(step)
}
const observer = new MutationObserver(() => {
const node = text()
if (!node || node.data === written) {
return
}
const target = readFigure(node.data)
const shown = written === null ? null : readFigure(written)
written = node.data
count(shown?.value ?? 0, target)
})
observer.observe(el, { characterData: true, childList: true, subtree: true })
count(0, written === null ? null : readFigure(written))
cleanup(() => {
cancelAnimationFrame(frame)
observer.disconnect()
})
})
})
+5 -2
View File
@@ -3,6 +3,9 @@
* *
* import '../../vendor/nonameweb/livewire-material/resources/js/material.js' * import '../../vendor/nonameweb/livewire-material/resources/js/material.js'
* *
* Alpine ships with Livewire, so this only registers onto it: the theme store, the * Alpine ships with Livewire, so these only register onto it (on `alpine:init`). A module
* snackbar listener and the list-row behaviour arrive in 0.2.0. * script runs before Livewire starts Alpine, and wire:navigate keeps it rather than running
* it again, so nothing registers twice.
*/ */
import './theme.js'
import './figure.js'
+62
View File
@@ -0,0 +1,62 @@
/**
* The theme, as one piece of state every toggle on the page shares.
*
* <x-theme-script> has already decided the theme before this runs; the store starts from
* what it wrote on <html> (data-theme-choice, data-theme, data-theme-key). `choice` is what
* the visitor picked — light, dark or system — and `resolved` is what is showing.
*
* `value` is an accessor, so binding a control with `x-model="$store.theme.value"` goes
* through the same write as `set()` and `toggle()`: the attributes, then localStorage.
*/
document.addEventListener('alpine:init', () => {
const root = document.documentElement
const media = window.matchMedia('(prefers-color-scheme: dark)')
const choices = ['light', 'dark', 'system']
const resolve = (choice) => (choice === 'system' ? (media.matches ? 'dark' : 'light') : choice)
window.Alpine.store('theme', {
choice: choices.includes(root.dataset.themeChoice) ? root.dataset.themeChoice : 'system',
resolved: root.dataset.theme === 'dark' ? 'dark' : 'light',
get value() {
return this.choice
},
set value(choice) {
this.set(choice)
},
set(choice) {
if (!choices.includes(choice)) {
return
}
this.choice = choice
this.resolved = resolve(choice)
root.dataset.themeChoice = choice
root.dataset.theme = this.resolved
try {
localStorage.setItem(root.dataset.themeKey || 'material-theme', choice)
} catch {
// Blocked storage: the page still switches, it just will not remember.
}
},
toggle() {
this.set(this.resolved === 'dark' ? 'light' : 'dark')
},
})
// The head script repaints on an OS change while the choice is `system`; this keeps the
// store's `resolved` — which a toggle's icon reads — in step with it.
media.addEventListener('change', () => {
const theme = window.Alpine.store('theme')
if (theme.choice === 'system') {
theme.resolved = resolve('system')
}
})
})
File diff suppressed because one or more lines are too long
+1
View File
@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 100" fill="currentColor"><path d="M16.1 16.1C24.7 7.4 36.7 2 50 2C76.5 2 98 23.5 98 50C98 61.5 98 72.9 98 84.4C98 91.9 91.9 98 84.4 98C61.5 98 38.5 98 15.6 98C8.1 98 2 91.9 2 84.4C2 72.9 2 61.5 2 50C2 36.7 7.4 24.7 16.1 16.1Z"/></svg>

After

Width:  |  Height:  |  Size: 292 B

+1
View File
@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 100" fill="currentColor"><path d="M50 88.2C47.8 88.1 45.6 88.4 43.4 88.9C37.6 90.1 31.9 91.4 26.1 92.7C24.6 93 23.1 93.2 21.7 93.2C10.6 93.2 2 83.8 2 73.4C2 70 2.9 66.6 4.8 63.3C8.1 57.7 11.4 52.2 14.7 46.6C30.4 20 38.3 6.7 50 6.7C61.7 6.7 69.6 19.9 85.3 46.5C88.6 51.9 91.9 57.4 95.1 62.8C97.1 66.2 98 69.7 98 73.1C98 83.8 89.3 93.3 78 93.3C76.4 93.3 74.9 93.2 73.2 92.8C67.7 91.5 62.2 90.2 56.6 88.9C54.5 88.4 52.2 88.2 50 88.2Z"/></svg>

After

Width:  |  Height:  |  Size: 497 B

+1
View File
@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 100" fill="currentColor"><path d="M45.6 29.5C45.8 29.5 46.1 29.3 46.1 29C47.2 20.3 48.3 11.6 49.3 2.8C49.4 2.1 50.6 2.1 50.7 2.8C51.8 11.6 52.9 20.3 54 29C54 29.6 54.9 29.8 55.2 29.2C59.7 21.7 64.3 14.2 68.8 6.7C69.2 6 70.2 6.5 70 7.2C67.5 15.6 64.9 24 62.4 32.4C62.2 33.1 62.9 33.6 63.4 33.2C70.6 28.2 77.8 23.2 85 18.1C85.6 17.7 86.4 18.5 85.9 19.1C80.1 25.8 74.4 32.4 68.7 39C68.2 39.5 68.7 40.3 69.3 40.1C77.9 38.5 86.5 36.8 95.1 35.2C95.9 35 96.3 36.1 95.5 36.4C87.6 40.2 79.7 43.9 71.7 47.6C71.1 47.9 71.2 48.8 71.8 48.9C80.4 50.9 88.9 52.9 97.5 54.8C98.3 55 98.1 56.2 97.3 56.2C88.6 56.4 79.8 56.5 71 56.7C70.4 56.7 70.1 57.5 70.6 57.9C77.6 63.2 84.6 68.5 91.6 73.8C92.2 74.3 91.7 75.2 90.9 74.9C82.9 71.5 74.8 68.1 66.7 64.7C66.1 64.5 65.5 65.1 65.8 65.7C70.1 73.3 74.3 81 78.6 88.7C78.9 89.4 78 90.1 77.5 89.5C71.5 83.1 65.5 76.7 59.5 70.3C59 69.8 58.2 70.2 58.3 70.8C59.1 79.5 59.8 88.3 60.6 97C60.6 97.8 59.5 98 59.3 97.3C56.4 89 53.5 80.7 50.6 72.4C50.4 71.8 49.5 71.8 49.3 72.4C46.5 80.7 43.6 89 40.7 97.3C40.5 98 39.4 97.8 39.4 97C40.2 88.3 40.9 79.5 41.6 70.8C41.7 70.1 40.9 69.8 40.5 70.3C34.5 76.7 28.5 83.1 22.5 89.5C22 90.1 21.1 89.4 21.4 88.7C25.7 81 29.9 73.3 34.1 65.6C34.4 65.1 33.9 64.4 33.3 64.7C25.2 68.1 17.1 71.5 9 74.9C8.3 75.2 7.7 74.3 8.4 73.8C15.4 68.5 22.4 63.2 29.4 57.9C29.9 57.5 29.6 56.7 29 56.6C20.2 56.5 11.4 56.3 2.7 56.2C1.9 56.1 1.7 55 2.5 54.8C11.1 52.8 19.6 50.8 28.2 48.8C28.8 48.7 28.9 47.8 28.3 47.6C20.3 43.8 12.4 40.1 4.5 36.4C3.7 36.1 4.1 35 4.9 35.1C13.5 36.8 22.1 38.4 30.7 40.1C31.4 40.2 31.8 39.5 31.4 39C25.6 32.4 19.9 25.7 14.1 19.1C13.6 18.5 14.4 17.7 15 18.1C22.2 23.1 29.4 28.1 36.6 33.2C37.2 33.5 37.9 33 37.7 32.4C35.1 24 32.6 15.6 30 7.2C29.8 6.5 30.8 6 31.2 6.7C35.8 14.2 40.3 21.7 44.9 29.2C45 29.5 45.3 29.6 45.6 29.5Z"/></svg>

After

Width:  |  Height:  |  Size: 1.8 KiB

+1
View File
@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 100" fill="currentColor"><path d="M78.4 50C78.8 50.1 79.1 50.2 79.5 50.3C82.4 51.3 85.1 52.7 87.5 54.7C93.6 59.6 96.9 67.2 96.4 75.1C96.4 75.1 96.4 75.1 96.4 75.2C95.6 88 84.9 98 72 98C57.3 98 42.7 98 28 98C15.1 98 4.4 88 3.6 75.2C3.6 75.1 3.6 75.1 3.6 75.1C3.1 67.2 6.4 59.6 12.5 54.7C14.9 52.7 17.6 51.3 20.5 50.3C20.9 50.2 21.2 50.1 21.6 50C21.2 49.9 20.9 49.8 20.5 49.7C17.6 48.7 14.9 47.3 12.5 45.3C6.4 40.4 3.1 32.8 3.6 24.9C3.6 24.9 3.6 24.9 3.6 24.8C4.4 12 15.1 2 28 2C42.7 2 57.3 2 72 2C84.9 2 95.6 12 96.4 24.8C96.4 24.9 96.4 24.9 96.4 24.9C96.9 32.8 93.6 40.4 87.5 45.3C85.1 47.3 82.4 48.7 79.5 49.7C79.1 49.8 78.8 49.9 78.4 50Z"/></svg>

After

Width:  |  Height:  |  Size: 706 B

+1
View File
@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 100" fill="currentColor"><path d="M50 2C50.2 2 50.4 2.1 50.5 2.3C53.2 7.1 55.9 11.9 58.5 16.6C58.7 16.9 59.1 17 59.3 16.8C64 14 68.7 11.2 73.4 8.4C73.8 8.2 74.3 8.5 74.3 8.9C74.2 14.4 74.2 19.9 74.1 25.4C74.1 25.7 74.3 26 74.7 26C80.1 25.9 85.6 25.8 91.1 25.7C91.5 25.7 91.8 26.2 91.6 26.6C88.8 31.3 86 36 83.2 40.7C83 41 83.1 41.4 83.4 41.5C88.2 44.2 92.9 46.8 97.7 49.5C98.1 49.7 98.1 50.3 97.7 50.5C92.9 53.2 88.1 55.9 83.4 58.5C83.1 58.7 83 59.1 83.2 59.3C86 64 88.8 68.7 91.6 73.4C91.8 73.8 91.5 74.3 91.1 74.3C85.6 74.2 80.1 74.2 74.6 74.1C74.3 74.1 74 74.3 74 74.7C74.1 80.1 74.2 85.6 74.3 91.1C74.3 91.5 73.8 91.8 73.4 91.6C68.7 88.8 64 86 59.3 83.2C59 83 58.6 83.1 58.5 83.4C55.8 88.2 53.2 92.9 50.5 97.7C50.3 98.1 49.7 98.1 49.5 97.7C46.8 92.9 44.1 88.1 41.5 83.4C41.3 83.1 40.9 83 40.7 83.2C36 86 31.3 88.8 26.6 91.6C26.2 91.8 25.7 91.5 25.7 91.1C25.8 85.6 25.8 80.1 25.9 74.6C25.9 74.3 25.7 74 25.3 74C19.9 74.1 14.4 74.2 8.9 74.3C8.5 74.3 8.2 73.8 8.4 73.4C11.2 68.7 14 64 16.8 59.3C17 59 16.9 58.6 16.6 58.5C11.8 55.8 7.1 53.2 2.3 50.5C1.9 50.3 1.9 49.7 2.3 49.5C7.1 46.8 11.9 44.1 16.6 41.5C16.9 41.3 17 40.9 16.8 40.7C14 36 11.2 31.3 8.4 26.6C8.2 26.2 8.5 25.7 8.9 25.7C14.4 25.8 19.9 25.8 25.4 25.9C25.7 25.9 26 25.7 26 25.3C25.9 19.9 25.8 14.4 25.7 8.9C25.7 8.5 26.2 8.2 26.6 8.4C31.3 11.2 36 14 40.7 16.8C41 17 41.4 16.9 41.5 16.6C44.2 11.8 46.8 7.1 49.5 2.3C49.6 2.1 49.8 2 50 2Z"/></svg>

After

Width:  |  Height:  |  Size: 1.4 KiB

+1
View File
@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 100" fill="currentColor"><path d="M98 50C98 55 97.2 60 95.7 64.8C92.5 74.5 86.4 82.9 78.2 88.8C70 94.8 60.1 98 50 98C39.9 98 30 94.8 21.8 88.8C13.6 82.9 7.5 74.5 4.3 64.8C1.2 55.2 1.2 44.8 4.3 35.2C7.5 25.5 13.6 17.1 21.8 11.2C30 5.2 39.9 2 50 2C60.1 2 70 5.2 78.2 11.2C86.4 17.1 92.5 25.5 95.7 35.2C97.2 40 98 45 98 50Z"/></svg>

After

Width:  |  Height:  |  Size: 387 B

+1
View File
@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 100" fill="currentColor"><path d="M19.6 80.7C17.3 79.3 15.4 77.4 14 74.9C10.6 68.8 7.1 62.7 3.7 56.6C1.4 52.5 1.4 47.5 3.7 43.4C7.1 37.3 10.5 31.2 13.9 25.1C16.6 20.2 21.7 17.2 27.3 17.2C42.4 17.2 57.5 17.2 72.7 17.2C78.2 17.2 83.3 20.2 86 25.1C89.4 31.2 92.9 37.3 96.3 43.4C98.6 47.5 98.6 52.5 96.3 56.6C92.9 62.7 89.5 68.8 86.1 74.9C83.4 79.8 78.3 82.8 72.7 82.8C57.6 82.8 42.5 82.8 27.3 82.8C24.6 82.8 21.9 82 19.6 80.7Z"/></svg>

After

Width:  |  Height:  |  Size: 490 B

+1
View File
@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 100" fill="currentColor"><path d="M50 9.1C50.5 8.7 51 8.4 51.5 8C56.7 4 62.9 2 69.1 2C76.5 2 83.9 4.9 89.5 10.5C95.1 16.1 98 23.5 98 30.9C98 37.1 96 43.3 92 48.5C91.6 49 91.3 49.5 90.9 50C91.3 50.5 91.6 51 92 51.5C96 56.7 98 62.9 98 69.1C98 76.5 95.1 83.9 89.5 89.5C83.9 95.1 76.5 98 69.1 98C62.9 98 56.7 96 51.5 92C51 91.6 50.5 91.3 50 90.9C49.5 91.3 49 91.6 48.5 92C43.3 96 37.1 98 30.9 98C23.5 98 16.1 95.1 10.5 89.5C4.9 83.9 2 76.5 2 69.1C2 62.9 4 56.7 8 51.5C8.4 51 8.7 50.5 9.1 50C8.7 49.5 8.4 49 8 48.5C4 43.3 2 37.1 2 30.9C2 23.5 4.9 16.1 10.5 10.5C16.1 4.9 23.5 2 30.9 2C37.1 2 43.3 4 48.5 8C49 8.4 49.5 8.7 50 9.1Z"/></svg>

After

Width:  |  Height:  |  Size: 691 B

+1
View File
@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 100" fill="currentColor"><path d="M50 5.6C50.8 5.2 51.5 4.8 52.3 4.4C55.3 2.8 58.5 2 61.6 2C70.4 2 78.6 7.9 81 17.1C81.1 17.6 81.3 18.1 81.4 18.6C82.2 18.8 83 19.1 83.8 19.3C92.6 22 98 30.1 98 38.5C98 41.9 97.1 45.4 95.2 48.7C94.9 49.1 94.7 49.6 94.4 50C94.8 50.8 95.2 51.5 95.6 52.3C97.2 55.3 98 58.5 98 61.6C98 70.4 92.1 78.6 82.9 81C82.4 81.1 81.9 81.3 81.4 81.4C81.2 82.2 80.9 83 80.7 83.8C78 92.6 69.9 98 61.5 98C58.1 98 54.6 97.1 51.3 95.2C50.9 94.9 50.4 94.7 50 94.4C49.2 94.8 48.5 95.2 47.7 95.6C44.7 97.2 41.5 98 38.4 98C29.6 98 21.4 92.1 19 82.9C18.9 82.4 18.7 81.9 18.6 81.4C17.8 81.2 17 80.9 16.2 80.7C7.4 78 2 69.9 2 61.5C2 58.1 2.9 54.6 4.8 51.3C5.1 50.9 5.3 50.4 5.6 50C5.2 49.2 4.8 48.5 4.4 47.7C2.8 44.7 2 41.5 2 38.4C2 29.6 7.9 21.4 17.1 19C17.6 18.9 18.1 18.7 18.6 18.6C18.8 17.8 19.1 17 19.3 16.2C22 7.4 30.1 2 38.5 2C41.9 2 45.4 2.9 48.7 4.8C49.1 5.1 49.6 5.3 50 5.6Z"/></svg>

After

Width:  |  Height:  |  Size: 955 B

+1
View File
@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 100" fill="currentColor"><path d="M50 2C52.5 2 55 3 56.9 5C59.3 7.7 63.1 8.7 66.5 7.6C71.8 6 77.3 9.1 78.4 14.5C79.2 18 82 20.8 85.5 21.6C90.9 22.7 94 28.2 92.4 33.5C91.3 36.9 92.3 40.7 95 43.1C99 46.8 99 53.2 95 56.9C92.3 59.3 91.3 63.1 92.4 66.5C94 71.8 90.9 77.3 85.5 78.4C82 79.2 79.2 82 78.4 85.5C77.3 90.9 71.8 94 66.5 92.4C63.1 91.3 59.3 92.3 56.9 95C53.2 99 46.8 99 43.1 95C40.7 92.3 36.9 91.3 33.5 92.4C28.2 94 22.7 90.9 21.6 85.5C20.8 82 18 79.2 14.5 78.4C9.1 77.3 6 71.8 7.6 66.5C8.7 63.1 7.7 59.3 5 56.9C1 53.2 1 46.8 5 43.1C7.7 40.7 8.7 36.9 7.6 33.5C6 28.2 9.1 22.7 14.5 21.6C18 20.8 20.8 18 21.6 14.5C22.7 9.1 28.2 6 33.5 7.6C36.9 8.7 40.7 7.7 43.1 5C45 3 47.5 2 50 2Z"/></svg>

After

Width:  |  Height:  |  Size: 750 B

+1
View File
@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 100" fill="currentColor"><path d="M90.7 90.6C86.2 95.1 80 98 73.3 98C70.1 98 66.7 97.3 63.4 95.9C61.9 95.2 60.4 94.6 58.9 93.9C53.2 91.5 46.8 91.5 41.1 94C39.7 94.6 38.2 95.2 36.7 95.9C33.3 97.3 30 98 26.7 98C13.3 98 2 86.7 2 73.3C2 70.1 2.7 66.7 4.1 63.4C4.8 61.9 5.4 60.4 6.1 58.9C8.5 53.2 8.5 46.8 6 41.1C5.4 39.7 4.8 38.2 4.1 36.7C2.7 33.3 2 30 2 26.7C2 13.3 13.3 2 26.7 2C29.9 2 33.3 2.7 36.6 4.1C38.1 4.8 39.6 5.4 41.1 6.1C46.8 8.5 53.2 8.5 58.9 6C60.3 5.4 61.8 4.8 63.3 4.1C66.7 2.7 70 2 73.3 2C86.7 2 98 13.3 98 26.7C98 29.9 97.3 33.3 95.9 36.6C95.2 38.1 94.6 39.6 93.9 41.1C91.5 46.8 91.5 53.2 94 58.9C94.6 60.3 95.2 61.8 95.9 63.3C97.3 66.7 98 70 98 73.3C98 80 95.2 86.1 90.7 90.6Z"/></svg>

After

Width:  |  Height:  |  Size: 758 B

+1
View File
@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 100" fill="currentColor"><path d="M71.8 87.5C70.1 88.5 68.5 89.7 67.1 91C67 91.1 67 91.1 66.9 91.2C62.2 95.7 56.1 98 50 98C43.9 98 37.8 95.7 33.1 91.2C30.3 88.4 26.8 86.4 23 85.3C22.9 85.3 22.9 85.3 22.8 85.3C10.2 81.6 2.7 68.7 5.9 55.9C6.9 52.1 6.9 48.1 5.9 44.3C5.9 44.2 5.9 44.1 5.9 44.1C2.7 31.3 10.2 18.4 22.8 14.8C26.6 13.7 30.1 11.7 32.9 9C33 8.9 33 8.9 33.1 8.8C37.8 4.3 43.9 2 50 2C56.1 2 62.2 4.3 66.9 8.8C69.7 11.6 73.2 13.6 77 14.7C77.1 14.7 77.1 14.7 77.2 14.7C89.8 18.4 97.3 31.3 94.1 44.1C93.1 47.9 93.1 51.9 94.1 55.7C94.1 55.8 94.1 55.9 94.1 55.9C97.3 68.7 89.8 81.6 77.2 85.2C75.3 85.8 73.5 86.5 71.8 87.5Z"/></svg>

After

Width:  |  Height:  |  Size: 691 B

+1
View File
@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 100" fill="currentColor"><path d="M50 2.7C54.9 2.7 59.9 4.6 63.7 8.4C66.8 11.5 70.8 13.4 75.2 13.9C85.8 15.1 93.4 24.7 92.2 35.3C91.7 39.6 92.7 44 95 47.7C97 50.9 98 54.5 98 58C98 64.4 94.8 70.7 89 74.4C85.3 76.7 82.5 80.2 81 84.3C77.5 94.4 66.5 99.7 56.4 96.2C52.2 94.8 47.8 94.8 43.6 96.2C33.5 99.7 22.5 94.4 19 84.3C17.5 80.2 14.7 76.7 11 74.4C5.2 70.7 2 64.4 2 58C2 54.5 3 50.9 5 47.7C7.3 44 8.3 39.6 7.8 35.3C6.6 24.7 14.2 15.1 24.8 13.9C29.2 13.4 33.2 11.5 36.3 8.4C40.1 4.6 45.1 2.7 50 2.7Z"/></svg>

After

Width:  |  Height:  |  Size: 564 B

+1
View File
@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 100" fill="currentColor"><path d="M50 2.5C53.6 2.5 57.3 3.8 60.2 6.4C63.1 9.1 66.9 10.5 70.9 10.3C78.7 10 85.4 15.7 86.4 23.4C87 27.3 89 30.8 92.1 33.3C98.3 38 99.8 46.7 95.7 53.3C93.6 56.6 92.9 60.6 93.7 64.5C95.3 72.1 90.9 79.7 83.5 82.1C79.8 83.3 76.6 85.9 74.8 89.4C71.2 96.3 62.9 99.3 55.7 96.4C52.1 94.9 47.9 94.9 44.3 96.4C37.1 99.3 28.8 96.3 25.2 89.4C23.4 85.9 20.2 83.3 16.5 82.1C9.1 79.7 4.7 72.1 6.3 64.5C7.1 60.6 6.4 56.6 4.3 53.3C0.2 46.7 1.7 38 7.9 33.3C11 30.8 13 27.3 13.6 23.4C14.6 15.7 21.3 10 29.1 10.3C33.1 10.5 36.9 9.1 39.8 6.4C42.7 3.8 46.4 2.5 50 2.5Z"/></svg>

After

Width:  |  Height:  |  Size: 643 B

+1
View File
@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 100" fill="currentColor"><path d="M50 98C47.4 98 44.7 97.3 42.4 95.8C40.1 94.5 37.6 91.2 32.5 84.6C26 76.2 19.5 67.7 13 59.3C8.8 53.8 8.8 46.2 13 40.7C19.5 32.3 26 23.8 32.5 15.4C37.6 8.8 40.1 5.5 42.4 4.2C47.1 1.3 52.9 1.3 57.6 4.2C59.9 5.5 62.4 8.8 67.5 15.4C74 23.8 80.5 32.3 87 40.7C91.2 46.2 91.2 53.8 87 59.3C80.5 67.7 74 76.2 67.5 84.6C62.4 91.2 59.9 94.5 57.6 95.8C55.3 97.3 52.6 98 50 98Z"/></svg>

After

Width:  |  Height:  |  Size: 464 B

+1
View File
@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 100" fill="currentColor"><path d="M94 93.7C92.3 95.5 90 96.9 87.5 97.5C85.7 98 83 98 77.8 98C57.3 98 36.9 98 16.5 98C8.5 98 2 91.5 2 83.5C2 61.1 2 38.6 2 16.2C2 8.2 8.6 1.8 16.6 2C18.6 2.1 20.6 2.1 22.6 2.1C63.7 3.1 96.8 36.1 97.9 77.2C97.9 77.2 97.9 77.3 97.9 77.3C98 82.6 98.1 85.3 97.7 87.1C97.1 89.6 95.8 91.9 94 93.7Z"/></svg>

After

Width:  |  Height:  |  Size: 389 B

+1
View File
@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 100" fill="currentColor"><path d="M37.5 19.9C38.3 17.4 39.2 14.8 40 12.3C41.3 8.6 43.6 5.4 46.7 3C47.5 2.3 48.6 2 49.6 2C49.8 2 50.1 2 50.3 2C51.4 2 52.4 2.3 53.2 3C56.3 5.3 58.7 8.6 59.9 12.3C60.7 14.8 61.6 17.3 62.4 19.9C64.8 18.7 67.2 17.5 69.6 16.3C73.1 14.5 77 13.9 80.9 14.4C81.9 14.6 82.9 15 83.7 15.8C83.8 15.9 84 16.1 84.2 16.3C84.9 17 85.4 18 85.5 19C86.1 22.9 85.4 26.8 83.7 30.3C82.5 32.7 81.3 35.1 80.1 37.5C82.6 38.3 85.2 39.2 87.7 40C91.4 41.3 94.6 43.6 97 46.7C97.7 47.5 98 48.6 98 49.6C98 49.8 98 50.1 98 50.3C98 51.4 97.7 52.4 97 53.2C94.7 56.3 91.4 58.7 87.7 59.9C85.2 60.7 82.7 61.6 80.1 62.4C81.3 64.8 82.5 67.2 83.7 69.6C85.5 73.1 86.1 77 85.6 80.9C85.4 81.9 85 82.9 84.2 83.7C84.1 83.8 83.9 84 83.7 84.2C83 84.9 82 85.4 81 85.5C77.1 86.1 73.2 85.4 69.7 83.7C67.3 82.5 64.9 81.3 62.5 80.1C61.7 82.6 60.8 85.2 60 87.7C58.7 91.4 56.4 94.6 53.3 97C52.5 97.7 51.4 98 50.4 98C50.2 98 49.9 98 49.7 98C48.6 98 47.6 97.7 46.8 97C43.7 94.7 41.3 91.4 40.1 87.7C39.3 85.2 38.4 82.7 37.6 80.1C35.2 81.3 32.8 82.5 30.4 83.7C26.9 85.5 23 86.1 19.1 85.6C18.1 85.4 17.1 85 16.3 84.2C16.2 84.1 16 83.9 15.8 83.7C15.1 83 14.6 82 14.5 81C13.9 77.1 14.6 73.2 16.3 69.7C17.5 67.3 18.7 64.9 19.9 62.5C17.4 61.7 14.8 60.8 12.3 60C8.6 58.7 5.4 56.4 3 53.3C2.3 52.5 2 51.4 2 50.4C2 50.2 2 49.9 2 49.7C2 48.6 2.3 47.6 3 46.8C5.3 43.7 8.6 41.3 12.3 40.1C14.8 39.3 17.3 38.4 19.9 37.6C18.7 35.2 17.5 32.8 16.3 30.4C14.5 26.9 13.9 23 14.4 19.1C14.6 18.1 15 17.1 15.8 16.3C15.9 16.2 16.1 16 16.3 15.8C17 15.1 18 14.6 19 14.5C22.9 13.9 26.8 14.6 30.3 16.3C32.7 17.5 35.1 18.7 37.5 19.9Z"/></svg>

After

Width:  |  Height:  |  Size: 1.6 KiB

+1
View File
@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 100" fill="currentColor"><path d="M49.9 98C49.2 98 48.4 98 47.6 97.9C43.8 97.5 40.2 95.8 32.9 92.5C26.9 89.8 21 87 15.1 84.3C7 80.6 2.4 72.1 3.7 63.3C5 54.2 6.3 45.1 7.7 36C8.5 30.1 11.7 24.8 16.6 21.4C23.8 16.2 31 11 38.3 5.8C41.7 3.3 45.9 2 50.1 2C54.4 2 58.5 3.4 61.9 5.8C69.2 11.1 76.4 16.3 83.6 21.5C88.4 25 91.6 30.3 92.5 36.2C93.8 45.3 95.1 54.4 96.3 63.5C97.6 72.2 92.9 80.8 84.8 84.5C78.9 87.2 73 89.8 67 92.5C59.7 95.9 56.1 97.5 52.3 97.9C51.5 98 50.7 98 49.9 98Z"/></svg>

After

Width:  |  Height:  |  Size: 540 B

+1
View File
@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 100" fill="currentColor"><path d="M50 2C76.5 2 98 23.5 98 50C98 59.5 98 69 98 78.5C98 89.9 88.7 98 78.5 98C75.4 98 72.2 97.2 69.2 95.6C67 94.3 64.8 93.1 62.6 91.9C59 90 55.1 89 51.1 89C50.4 89 49.6 89 48.9 89C44.9 89 41 90 37.4 91.9C35.2 93.1 33 94.3 30.8 95.6C27.8 97.2 24.6 98 21.5 98C11.3 98 2 89.9 2 78.5C2 69 2 59.5 2 50C2 23.5 23.5 2 50 2Z"/></svg>

After

Width:  |  Height:  |  Size: 412 B

+1
View File
@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 100" fill="currentColor"><path d="M50 27.4C50.2 27.4 50.3 27.4 50.4 27.2C54.3 22.8 58.1 18.5 61.9 14.1C70.3 4.5 85.4 4.8 93.4 14.8C99.7 22.8 99.5 34.1 93 41.9C78.7 58.9 64.4 75.8 50.2 92.8C50.1 92.9 50.1 92.9 50 92.9C49.9 92.9 49.9 92.9 49.8 92.8C35.6 75.8 21.3 58.9 7 41.9C0.5 34.1 0.3 22.8 6.6 14.8C14.6 4.8 29.7 4.5 38.1 14.1C41.9 18.5 45.7 22.8 49.6 27.2C49.7 27.4 49.8 27.4 50 27.4Z"/></svg>

After

Width:  |  Height:  |  Size: 454 B

+1
View File
@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 100" fill="currentColor"><path d="M90.4 9.6C93.8 12.9 96.1 17.3 97.3 22.6C99.5 33 96.7 45.9 89.4 58.4C82.1 71 71 82.1 58.4 89.4C45.9 96.7 33 99.5 22.6 97.3C12.1 95 5 87.9 2.7 77.4C0.5 67 3.3 54.1 10.6 41.6C17.9 29 29 17.9 41.6 10.6C54.1 3.3 67 0.5 77.4 2.7C82.7 3.9 87.1 6.2 90.4 9.6Z"/></svg>

After

Width:  |  Height:  |  Size: 351 B

+1
View File
@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 100" fill="currentColor"><path d="M50 4.8C53.3 4.8 56.7 5.8 59.5 7.9C70.1 15.3 80.7 22.8 91.3 30.3C97 34.3 99.4 41.6 97.2 48.2C93.2 60.1 89.2 72.1 85.2 84.1C83 90.7 76.8 95.2 69.8 95.2C56.6 95.2 43.4 95.2 30.2 95.2C23.2 95.2 17 90.7 14.8 84.1C10.8 72.1 6.8 60.1 2.8 48.2C0.6 41.6 3 34.3 8.7 30.3C19.3 22.8 29.9 15.3 40.5 7.9C43.3 5.8 46.7 4.8 50 4.8Z"/></svg>

After

Width:  |  Height:  |  Size: 417 B

+1
View File
@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 100" fill="currentColor"><path d="M85.8 14.2C92.3 20.6 96.6 29.2 97.6 38.9C97.7 40.3 97.9 41.7 98 43.1C97.9 54.2 93.5 64.8 85.6 72.7C81.3 77 77 81.3 72.7 85.6C64.8 93.5 54.2 97.9 43.1 98C41.7 97.9 40.3 97.7 38.9 97.6C19.6 95.6 4.4 80.4 2.4 61.1C2.3 59.7 2.1 58.3 2 56.9C2.1 45.8 6.5 35.2 14.4 27.3C18.7 23 23 18.7 27.3 14.4C35.2 6.5 45.8 2.1 56.9 2C58.3 2.1 59.7 2.3 61.1 2.4C70.8 3.4 79.4 7.7 85.8 14.2Z"/></svg>

After

Width:  |  Height:  |  Size: 471 B

+1
View File
@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 100" fill="currentColor"><path d="M50 2C56.5 2 63.1 2 69.6 2C69.6 4.1 69.6 6.2 69.6 8.2C74 8.2 78.5 8.2 82.9 8.2C82.9 10.9 82.9 13.6 82.9 16.2C85.6 16.2 88.2 16.2 90.9 16.2C90.9 20.9 90.9 25.7 90.9 30.4C93.3 30.4 95.6 30.4 98 30.4C98 43.5 98 56.5 98 69.6C95.6 69.6 93.3 69.6 90.9 69.6C90.9 74.3 90.9 79.1 90.9 83.8C88.2 83.8 85.6 83.8 82.9 83.8C82.9 86.4 82.9 89.1 82.9 91.8C78.5 91.8 74 91.8 69.6 91.8C69.6 93.8 69.6 95.9 69.6 98C63.1 98 56.5 98 50 98C43.5 98 36.9 98 30.4 98C30.4 95.9 30.4 93.8 30.4 91.8C26 91.8 21.5 91.8 17.1 91.8C17.1 89.1 17.1 86.4 17.1 83.8C14.4 83.8 11.8 83.8 9.1 83.8C9.1 79.1 9.1 74.3 9.1 69.6C6.7 69.6 4.4 69.6 2 69.6C2 56.5 2 43.5 2 30.4C4.4 30.4 6.7 30.4 9.1 30.4C9.1 25.7 9.1 20.9 9.1 16.2C11.8 16.2 14.4 16.2 17.1 16.2C17.1 13.6 17.1 10.9 17.1 8.2C21.5 8.2 26 8.2 30.4 8.2C30.4 6.2 30.4 4.1 30.4 2C36.9 2 43.5 2 50 2Z"/></svg>

After

Width:  |  Height:  |  Size: 916 B

+1
View File
@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 100" fill="currentColor"><path d="M12.7 50C12.8 34 12.8 18 12.9 2C18.5 2 24.1 2 29.6 2C29.6 4.8 29.6 7.6 29.6 10.4C33.9 10.4 38.2 10.4 42.5 10.4C42.5 13 42.5 15.7 42.5 18.3C47 18.3 51.4 18.3 55.9 18.3C55.9 21.4 55.9 24.4 55.9 27.4C59.5 27.4 63.2 27.4 66.8 27.4C66.8 30 66.9 32.5 66.9 35C70.5 35 74.2 35 77.8 35C77.8 38.1 77.8 41.1 77.8 44.1C81 44.1 84.2 44.1 87.3 44.1C87.3 48 87.3 52 87.3 55.9C84.2 55.9 81 55.9 77.8 55.9C77.8 58.9 77.8 61.9 77.8 65C74.2 65 70.5 65 66.9 65C66.9 67.5 66.8 70 66.8 72.6C63.2 72.6 59.5 72.6 55.9 72.6C55.9 75.6 55.9 78.6 55.9 81.7C51.4 81.7 47 81.7 42.5 81.7C42.5 84.3 42.5 87 42.5 89.6C38.2 89.6 33.9 89.6 29.6 89.6C29.6 92.4 29.6 95.2 29.6 98C24.1 98 18.5 98 12.9 98C12.8 82 12.8 66 12.7 50Z"/></svg>

After

Width:  |  Height:  |  Size: 792 B

+1
View File
@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 100" fill="currentColor"><path d="M76.8 23.2C80 26.4 81.7 31.2 80.5 36.3C81 36.2 81.5 36.1 82 36.1C90.5 34.9 98 41.5 98 50C98 58.5 90.5 65.1 82 63.9C81.5 63.9 81 63.8 80.5 63.7C82.8 73.8 73.8 82.8 63.7 80.5C63.8 81 63.9 81.5 63.9 82C65.1 90.5 58.5 98 50 98C41.5 98 34.9 90.5 36.1 82C36.1 81.5 36.2 81 36.3 80.5C26.2 82.8 17.2 73.8 19.5 63.7C19 63.8 18.5 63.9 18 63.9C9.5 65.1 2 58.5 2 50C2 41.5 9.5 34.9 18 36.1C18.5 36.1 19 36.2 19.5 36.3C17.2 26.2 26.2 17.2 36.3 19.5C36.2 19 36.1 18.5 36.1 18C34.9 9.5 41.5 2 50 2C58.5 2 65.1 9.5 63.9 18C63.9 18.5 63.8 19 63.7 19.5C68.8 18.3 73.6 20 76.8 23.2Z"/></svg>

After

Width:  |  Height:  |  Size: 664 B

+1
View File
@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 100" fill="currentColor"><path d="M50 18.3C50.6 17.5 51.1 16.6 51.7 15.8C53.3 13.3 56.7 11.8 60.3 11.9C63.9 12.1 67.2 13.7 68.7 16.1C69 16.5 69.2 16.9 69.4 17.2C70.3 18.6 70.8 20 71 21.5C71 22.2 71.1 22.9 71.2 23.7C73.7 19.4 81.2 18.5 85.4 21.9C85.5 21.9 85.6 22 85.7 22.1C88.2 24.1 89.7 26.6 90 29.3C90 29.5 90 29.7 90.1 29.9C90.4 33.2 89.1 36.5 86.4 39.1C86.6 39.1 86.7 39.1 86.9 39.1C90.1 39 93.1 40.1 94.8 42.1C96.9 44.3 98 47 98 49.7C98 49.9 98 50.1 98 50.3C98 53 96.9 55.7 94.8 57.9C93.1 59.9 90.1 61 86.9 60.9C86.7 60.9 86.6 60.9 86.4 60.9C89.1 63.5 90.4 66.8 90.1 70.1C90 70.3 90 70.5 90 70.7C89.7 73.4 88.2 75.9 85.7 77.9C85.6 78 85.5 78.1 85.4 78.1C81.2 81.5 73.7 80.6 71.2 76.3C71.1 77.1 71 77.8 71 78.5C70.8 80 70.3 81.4 69.4 82.8C69.2 83.1 69 83.5 68.7 83.9C67.2 86.3 63.9 87.9 60.3 88.1C56.7 88.2 53.3 86.7 51.7 84.2C51.1 83.4 50.6 82.5 50 81.7C49.4 82.5 48.9 83.4 48.3 84.2C46.7 86.7 43.3 88.2 39.7 88.1C36.1 87.9 32.8 86.3 31.3 83.9C31 83.5 30.8 83.1 30.6 82.8C29.7 81.4 29.2 80 29 78.5C29 77.8 28.9 77.1 28.8 76.3C26.3 80.6 18.8 81.5 14.6 78.1C14.5 78.1 14.4 78 14.3 77.9C11.8 75.9 10.3 73.4 10 70.7C10 70.5 10 70.3 9.9 70.1C9.6 66.8 10.9 63.5 13.6 60.9C13.4 60.9 13.3 60.9 13.1 60.9C9.9 61 6.9 59.9 5.2 57.9C3.1 55.7 2 53 2 50.3C2 50.1 2 49.9 2 49.7C2 47 3.1 44.3 5.2 42.1C6.9 40.1 9.9 39 13.1 39.1C13.3 39.1 13.4 39.1 13.6 39.1C10.9 36.5 9.6 33.2 9.9 29.9C10 29.7 10 29.5 10 29.3C10.3 26.6 11.8 24.1 14.3 22.1C14.4 22 14.5 21.9 14.6 21.9C18.8 18.5 26.3 19.4 28.8 23.7C28.9 22.9 29 22.2 29 21.5C29.2 20 29.7 18.6 30.6 17.2C30.8 16.9 31 16.5 31.3 16.1C32.8 13.7 36.1 12.1 39.7 11.9C43.3 11.8 46.7 13.3 48.3 15.8C48.9 16.6 49.4 17.5 50 18.3Z"/></svg>

After

Width:  |  Height:  |  Size: 1.7 KiB

+1
View File
@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 100" fill="currentColor"><path d="M95.1 77.1C93.3 78.9 90.8 80 88 80C62.7 80 37.3 80 12 80C6.5 80 2 75.5 2 70C2 69.3 2 68.7 2 68C2 41.5 23.5 20 50 20C76.5 20 98 41.5 98 68C98 68.7 98 69.3 98 70C98 72.8 96.9 75.3 95.1 77.1Z"/></svg>

After

Width:  |  Height:  |  Size: 289 B

+1
View File
@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 100" fill="currentColor"><path d="M86.6 90.3C85.8 91 85 91.6 84.1 92.2C79.7 95 73.5 95 61.1 94.9C47.7 94.9 34.3 94.8 21 94.8C20.1 94.8 19.6 94.8 19.3 94.8C9.2 94.5 1.3 85.7 2 75.6C2.1 75.3 2.1 74.9 2.2 74C3.5 60.8 4.9 47.7 6.2 34.6C7.4 22.2 8.1 16.1 11.3 11.9C12.6 10.3 14.1 8.9 15.9 7.8C20.3 5 26.5 5 38.9 5.1C52.3 5.1 65.7 5.2 79 5.2C79.9 5.2 80.4 5.2 80.7 5.2C90.8 5.5 98.7 14.3 98 24.4C97.9 24.7 97.9 25.1 97.8 26C96.5 39.2 95.1 52.3 93.8 65.4C92.6 77.8 91.9 83.9 88.7 88.1C88 88.9 87.3 89.6 86.6 90.3Z"/></svg>

After

Width:  |  Height:  |  Size: 573 B

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 5.4 KiB

+1
View File
@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 100" fill="currentColor"><path d="M19.4 27.8C20.1 26.8 20.5 25.7 20.4 24.4C20.1 21.5 19.9 18.6 19.7 15.7C19.4 11.9 23.2 9.2 26.6 10.6C29.4 11.7 32.1 12.8 34.8 13.9C37.1 14.9 39.8 14 41 11.9C42.6 9.4 44.1 6.9 45.7 4.4C47.6 1.2 52.3 1.2 54.3 4.4C55.8 6.9 57.4 9.4 58.9 11.9C60.2 14 62.9 14.9 65.2 13.9C67.9 12.8 70.6 11.7 73.3 10.6C76.8 9.1 80.5 11.9 80.3 15.6C80 18.5 79.8 21.5 79.6 24.4C79.4 26.9 81.1 29.1 83.5 29.7C86.3 30.4 89.2 31.1 92 31.8C95.7 32.7 97.1 37.1 94.7 40C92.8 42.2 90.9 44.5 89 46.7C87.4 48.6 87.4 51.4 89 53.3C90.9 55.5 92.8 57.7 94.7 60C97.1 62.8 95.7 67.3 92.1 68.2C89.2 68.9 86.3 69.6 83.5 70.3C81.1 70.9 79.5 73.1 79.6 75.6C79.9 78.5 80.1 81.4 80.3 84.3C80.6 88.1 76.8 90.8 73.4 89.4C70.6 88.3 67.9 87.2 65.2 86.1C62.9 85.1 60.2 86 59 88.1C57.4 90.6 55.9 93.1 54.3 95.6C52.4 98.8 47.7 98.8 45.7 95.6C44.2 93.1 42.6 90.6 41.1 88.1C39.8 86 37.1 85.1 34.8 86.1C32.1 87.2 29.4 88.3 26.7 89.4C23.2 90.9 19.5 88.1 19.7 84.4C20 81.5 20.2 78.5 20.4 75.6C20.6 73.1 18.9 70.9 16.5 70.3C13.7 69.6 10.8 68.9 8 68.2C4.3 67.3 2.9 62.9 5.3 60C7.2 57.8 9.1 55.5 11 53.3C12.6 51.4 12.6 48.6 11 46.7C9.1 44.5 7.2 42.3 5.3 40C2.9 37.2 4.3 32.7 7.9 31.8C10.8 31.1 13.7 30.4 16.5 29.7C17.7 29.4 18.7 28.7 19.4 27.8Z"/></svg>

After

Width:  |  Height:  |  Size: 1.3 KiB

+1
View File
@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 100" fill="currentColor"><path d="M89.6 89.6C84.4 94.8 77.2 98 69.2 98C56.4 98 43.6 98 30.8 98C14.9 98 2 85.1 2 69.2C2 56.4 2 43.6 2 30.8C2 14.9 14.9 2 30.8 2C43.6 2 56.4 2 69.2 2C85.1 2 98 14.9 98 30.8C98 43.6 98 56.4 98 69.2C98 77.2 94.8 84.4 89.6 89.6Z"/></svg>

After

Width:  |  Height:  |  Size: 322 B

+1
View File
@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 100" fill="currentColor"><path d="M98 50C98 51.7 97.4 53.5 96.2 54.9C93.7 57.8 91.3 60.7 88.8 63.6C87.8 64.8 87.2 66.3 87 67.9C86.7 71.6 86.4 75.4 86.1 79.2C85.8 82.9 82.9 85.8 79.2 86.1C75.4 86.4 71.6 86.7 67.9 87C66.3 87.2 64.8 87.8 63.6 88.8C60.7 91.3 57.8 93.7 54.9 96.2C52.1 98.6 47.9 98.6 45.1 96.2C42.2 93.7 39.3 91.3 36.4 88.8C35.2 87.8 33.7 87.2 32.1 87C28.4 86.7 24.6 86.4 20.8 86.1C17.1 85.8 14.2 82.9 13.9 79.2C13.6 75.4 13.3 71.6 13 67.9C12.8 66.3 12.2 64.8 11.2 63.6C8.7 60.7 6.3 57.8 3.8 54.9C1.4 52.1 1.4 47.9 3.8 45.1C6.3 42.2 8.7 39.3 11.2 36.4C12.2 35.2 12.8 33.7 13 32.1C13.3 28.4 13.6 24.6 13.9 20.8C14.2 17.1 17.1 14.2 20.8 13.9C24.6 13.6 28.4 13.3 32.1 13C33.7 12.8 35.2 12.2 36.4 11.2C39.3 8.7 42.2 6.3 45.1 3.8C47.9 1.4 52.1 1.4 54.9 3.8C57.8 6.3 60.7 8.7 63.6 11.2C64.8 12.2 66.3 12.8 67.9 13C71.6 13.3 75.4 13.6 79.2 13.9C82.9 14.2 85.8 17.1 86.1 20.8C86.4 24.6 86.7 28.4 87 32.1C87.2 33.7 87.8 35.2 88.8 36.4C91.3 39.3 93.7 42.2 96.2 45.1C97.4 46.5 98 48.3 98 50Z"/></svg>

After

Width:  |  Height:  |  Size: 1.0 KiB

+1
View File
@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 100" fill="currentColor"><path d="M50 6.7C54.5 6.7 59 8.9 61.5 13.3C73.1 33.3 84.6 53.3 96.2 73.3C97.4 75.5 98 77.8 98 80C98 86.9 92.4 93.3 84.6 93.3C61.5 93.3 38.5 93.3 15.4 93.3C7.6 93.3 2 86.9 2 80C2 77.8 2.6 75.5 3.8 73.3C15.4 53.3 26.9 33.3 38.5 13.3C41 8.9 45.5 6.7 50 6.7Z"/></svg>

After

Width:  |  Height:  |  Size: 346 B

+1
View File
@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 100" fill="currentColor"><path d="M50 98C47.3 98 44.6 96.7 43 94C41.8 92.1 40.6 90.1 39.5 88.1C37.6 85.1 34 83.5 30.5 84.4C28.3 85 26 85.5 23.8 86.1C17.8 87.6 12.4 82.2 13.9 76.2C14.5 74 15 71.7 15.6 69.5C16.5 66 14.9 62.4 11.9 60.5C9.9 59.4 7.9 58.2 6 57C0.7 53.8 0.7 46.2 6 43C7.9 41.8 9.9 40.6 11.9 39.5C14.9 37.6 16.5 34 15.6 30.5C15 28.3 14.5 26 13.9 23.8C12.4 17.8 17.8 12.4 23.8 13.9C26 14.5 28.3 15 30.5 15.6C34 16.5 37.6 14.9 39.5 11.9C40.6 9.9 41.8 7.9 43 6C46.2 0.7 53.8 0.7 57 6C58.2 7.9 59.4 9.9 60.5 11.9C62.4 14.9 66 16.5 69.5 15.6C71.7 15 74 14.5 76.2 13.9C82.2 12.4 87.6 17.8 86.1 23.8C85.5 26 85 28.3 84.4 30.5C83.5 34 85.1 37.6 88.1 39.5C90.1 40.6 92.1 41.8 94 43C99.3 46.2 99.3 53.8 94 57C92.1 58.2 90.1 59.4 88.1 60.5C85.1 62.4 83.5 66 84.4 69.5C85 71.7 85.5 74 86.1 76.2C87.6 82.2 82.2 87.6 76.2 86.1C74 85.5 71.7 85 69.5 84.4C66 83.5 62.4 85.1 60.5 88.1C59.4 90.1 58.2 92.1 57 94C55.4 96.7 52.7 98 50 98Z"/></svg>

After

Width:  |  Height:  |  Size: 994 B

+1
View File
@@ -0,0 +1 @@
40a7a292a79d9394157e1ea24f83d52d5e17c556
+1
View File
@@ -0,0 +1 @@
<svg fill="currentColor" xmlns="http://www.w3.org/2000/svg" viewBox="0 -960 960 960"><path d="M240-540v150q0 13 8.5 21.5T270-360q13 0 21.5-8.5T300-390v-180q0-13-8.5-21.5T270-600h-40q-13 0-21.5 8.5T200-570q0 13 8.5 21.5T230-540h10Zm140 180h100q17 0 28.5-11.5T520-400v-160q0-17-11.5-28.5T480-600H380q-17 0-28.5 11.5T340-560v160q0 17 11.5 28.5T380-360Zm20-60v-120h60v120h-60Zm220-30 62 79q2 3 22 11 17 0 25-15.5t-3-29.5l-56-75 57-76q11-14 3-29t-26-15q-7 0-12.5 3t-9.5 8l-62 79v-60q0-13-8.5-21.5T590-600q-13 0-21.5 8.5T560-570v180q0 13 8.5 21.5T590-360q13 0 21.5-8.5T620-390v-60ZM200-120q-33 0-56.5-23.5T120-200v-560q0-33 23.5-56.5T200-840h560q33 0 56.5 23.5T840-760v560q0 33-23.5 56.5T760-120H200Z"/></svg>

After

Width:  |  Height:  |  Size: 703 B

+1
View File
@@ -0,0 +1 @@
<svg fill="currentColor" xmlns="http://www.w3.org/2000/svg" viewBox="0 -960 960 960"><path d="M340-680v150q0 13 8.5 21.5T370-500q13 0 21.5-8.5T400-530v-180q0-13-8.5-21.5T370-740h-60q-13 0-21.5 8.5T280-710q0 13 8.5 21.5T310-680h30Zm180 180h100q17 0 28.5-11.5T660-540v-160q0-17-11.5-28.5T620-740H520q-17 0-28.5 11.5T480-700v160q0 17 11.5 28.5T520-500Zm20-60v-120h60v120h-60ZM200-120q-33 0-56.5-23.5T120-200v-560q0-33 23.5-56.5T200-840h560q33 0 56.5 23.5T840-760v560q0 33-23.5 56.5T760-120H200Zm100-280h40v90q0 13 8.5 21.5T370-280q13 0 21.5-8.5T400-310v-90h40v150q0 13 8.5 21.5T470-220q13 0 21.5-8.5T500-250v-170q0-17-11.5-28.5T460-460H280q-17 0-28.5 11.5T240-420v170q0 13 8.5 21.5T270-220q13 0 21.5-8.5T300-250v-150Zm300 120h80q17 0 28.5-11.5T720-320v-100q0-17-11.5-28.5T680-460H570q-13 0-21.5 8.5T540-430v180q0 13 8.5 21.5T570-220q13 0 21.5-8.5T600-250v-30Zm0-60v-60h60v60h-60Z"/></svg>

After

Width:  |  Height:  |  Size: 885 B

+1
View File
@@ -0,0 +1 @@
<svg fill="currentColor" xmlns="http://www.w3.org/2000/svg" viewBox="0 -960 960 960"><path d="M380-680v150q0 13 8.5 21.5T410-500q13 0 21.5-8.5T440-530v-180q0-13-8.5-21.5T410-740h-60q-13 0-21.5 8.5T320-710q0 13 8.5 21.5T350-680h30Zm200 0v150q0 13 8.5 21.5T610-500q13 0 21.5-8.5T640-530v-180q0-13-8.5-21.5T610-740h-60q-13 0-21.5 8.5T520-710q0 13 8.5 21.5T550-680h30ZM200-120q-33 0-56.5-23.5T120-200v-560q0-33 23.5-56.5T200-840h560q33 0 56.5 23.5T840-760v560q0 33-23.5 56.5T760-120H200Zm100-280h40v90q0 13 8.5 21.5T370-280q13 0 21.5-8.5T400-310v-90h40v150q0 13 8.5 21.5T470-220q13 0 21.5-8.5T500-250v-170q0-17-11.5-28.5T460-460H280q-17 0-28.5 11.5T240-420v170q0 13 8.5 21.5T270-220q13 0 21.5-8.5T300-250v-150Zm300 120h80q17 0 28.5-11.5T720-320v-100q0-17-11.5-28.5T680-460H570q-13 0-21.5 8.5T540-430v180q0 13 8.5 21.5T570-220q13 0 21.5-8.5T600-250v-30Zm0-60v-60h60v60h-60Z"/></svg>

After

Width:  |  Height:  |  Size: 877 B

+1
View File
@@ -0,0 +1 @@
<svg fill="currentColor" xmlns="http://www.w3.org/2000/svg" viewBox="0 -960 960 960"><path d="M220-540h-30q-13 0-21.5-8.5T160-570q0-13 8.5-21.5T190-600h60q13 0 21.5 8.5T280-570v180q0 13-8.5 21.5T250-360q-13 0-21.5-8.5T220-390v-150Zm140 150v-70q0-17 11.5-28.5T400-500h80v-40h-90q-13 0-21.5-8.5T360-570q0-13 8.5-21.5T390-600h110q17 0 28.5 11.5T540-560v60q0 17-11.5 28.5T500-460h-80v40h90q13 0 21.5 8.5T540-390q0 13-8.5 21.5T510-360H390q-13 0-21.5-8.5T360-390Zm380 30H630q-13 0-21.5-8.5T600-390q0-13 8.5-21.5T630-420h90v-40h-60q-8 0-14-6t-6-14q0-8 6-14t14-6h60v-40h-90q-13 0-21.5-8.5T600-570q0-13 8.5-21.5T630-600h110q17 0 28.5 11.5T780-560v160q0 17-11.5 28.5T740-360Z"/></svg>

After

Width:  |  Height:  |  Size: 674 B

+1
View File
@@ -0,0 +1 @@
<svg fill="currentColor" xmlns="http://www.w3.org/2000/svg" viewBox="0 -960 960 960"><path d="M340-680v150q0 13 8.5 21.5T370-500q13 0 21.5-8.5T400-530v-180q0-13-8.5-21.5T370-740h-60q-13 0-21.5 8.5T280-710q0 13 8.5 21.5T310-680h30Zm200 120v-40h80q17 0 28.5-11.5T660-640v-60q0-17-11.5-28.5T620-740H510q-13 0-21.5 8.5T480-710q0 13 8.5 21.5T510-680h90v40h-80q-17 0-28.5 11.5T480-600v70q0 13 8.5 21.5T510-500h120q13 0 21.5-8.5T660-530q0-13-8.5-21.5T630-560h-90ZM200-120q-33 0-56.5-23.5T120-200v-560q0-33 23.5-56.5T200-840h560q33 0 56.5 23.5T840-760v560q0 33-23.5 56.5T760-120H200Zm100-280h40v90q0 13 8.5 21.5T370-280q13 0 21.5-8.5T400-310v-90h40v150q0 13 8.5 21.5T470-220q13 0 21.5-8.5T500-250v-170q0-17-11.5-28.5T460-460H280q-17 0-28.5 11.5T240-420v170q0 13 8.5 21.5T270-220q13 0 21.5-8.5T300-250v-150Zm300 120h80q17 0 28.5-11.5T720-320v-100q0-17-11.5-28.5T680-460H570q-13 0-21.5 8.5T540-430v180q0 13 8.5 21.5T570-220q13 0 21.5-8.5T600-250v-30Zm0-60v-60h60v60h-60Z"/></svg>

After

Width:  |  Height:  |  Size: 969 B

+1
View File
@@ -0,0 +1 @@
<svg fill="currentColor" xmlns="http://www.w3.org/2000/svg" viewBox="0 -960 960 960"><path d="M340-680v150q0 13 8.5 21.5T370-500q13 0 21.5-8.5T400-530v-180q0-13-8.5-21.5T370-740h-60q-13 0-21.5 8.5T280-710q0 13 8.5 21.5T310-680h30Zm260 120h-90q-13 0-21.5 8.5T480-530q0 13 8.5 21.5T510-500h110q17 0 28.5-11.5T660-540v-160q0-17-11.5-28.5T620-740H510q-13 0-21.5 8.5T480-710q0 13 8.5 21.5T510-680h90v40h-60q-8 0-14 6t-6 14q0 8 6 14t14 6h60v40ZM200-120q-33 0-56.5-23.5T120-200v-560q0-33 23.5-56.5T200-840h560q33 0 56.5 23.5T840-760v560q0 33-23.5 56.5T760-120H200Zm100-280h40v90q0 13 8.5 21.5T370-280q13 0 21.5-8.5T400-310v-90h40v150q0 13 8.5 21.5T470-220q13 0 21.5-8.5T500-250v-170q0-17-11.5-28.5T460-460H280q-17 0-28.5 11.5T240-420v170q0 13 8.5 21.5T270-220q13 0 21.5-8.5T300-250v-150Zm300 120h80q17 0 28.5-11.5T720-320v-100q0-17-11.5-28.5T680-460H570q-13 0-21.5 8.5T540-430v180q0 13 8.5 21.5T570-220q13 0 21.5-8.5T600-250v-30Zm0-60v-60h60v60h-60Z"/></svg>

After

Width:  |  Height:  |  Size: 951 B

+1
View File
@@ -0,0 +1 @@
<svg fill="currentColor" xmlns="http://www.w3.org/2000/svg" viewBox="0 -960 960 960"><path d="M340-680v150q0 13 8.5 21.5T370-500q13 0 21.5-8.5T400-530v-180q0-13-8.5-21.5T370-740h-60q-13 0-21.5 8.5T280-710q0 13 8.5 21.5T310-680h30Zm240 120v30q0 13 8.5 21.5T610-500q13 0 21.5-8.5T640-530v-30h10q13 0 21.5-8.5T680-590q0-13-8.5-21.5T650-620h-10v-90q0-13-8.5-21.5T610-740q-13 0-21.5 8.5T580-710v90h-60v-90q0-13-8.5-21.5T490-740q-13 0-21.5 8.5T460-710v120q0 13 8.5 21.5T490-560h90ZM200-120q-33 0-56.5-23.5T120-200v-560q0-33 23.5-56.5T200-840h560q33 0 56.5 23.5T840-760v560q0 33-23.5 56.5T760-120H200Zm100-280h40v90q0 13 8.5 21.5T370-280q13 0 21.5-8.5T400-310v-90h40v150q0 13 8.5 21.5T470-220q13 0 21.5-8.5T500-250v-170q0-17-11.5-28.5T460-460H280q-17 0-28.5 11.5T240-420v170q0 13 8.5 21.5T270-220q13 0 21.5-8.5T300-250v-150Zm300 120h80q17 0 28.5-11.5T720-320v-100q0-17-11.5-28.5T680-460H570q-13 0-21.5 8.5T540-430v180q0 13 8.5 21.5T570-220q13 0 21.5-8.5T600-250v-30Zm0-60v-60h60v60h-60Z"/></svg>

After

Width:  |  Height:  |  Size: 988 B

+1
View File
@@ -0,0 +1 @@
<svg fill="currentColor" xmlns="http://www.w3.org/2000/svg" viewBox="0 -960 960 960"><path d="M340-680v150q0 13 8.5 21.5T370-500q13 0 21.5-8.5T400-530v-180q0-13-8.5-21.5T370-740h-60q-13 0-21.5 8.5T280-710q0 13 8.5 21.5T310-680h30Zm260 120h-90q-13 0-21.5 8.5T480-530q0 13 8.5 21.5T510-500h110q17 0 28.5-11.5T660-540v-60q0-17-11.5-28.5T620-640h-80v-40h90q13 0 21.5-8.5T660-710q0-13-8.5-21.5T630-740H510q-13 0-21.5 8.5T480-710v80q0 13 8.5 21.5T510-600h90v40ZM200-120q-33 0-56.5-23.5T120-200v-560q0-33 23.5-56.5T200-840h560q33 0 56.5 23.5T840-760v560q0 33-23.5 56.5T760-120H200Zm100-280h40v90q0 13 8.5 21.5T370-280q13 0 21.5-8.5T400-310v-90h40v150q0 13 8.5 21.5T470-220q13 0 21.5-8.5T500-250v-170q0-17-11.5-28.5T460-460H280q-17 0-28.5 11.5T240-420v170q0 13 8.5 21.5T270-220q13 0 21.5-8.5T300-250v-150Zm300 120h80q17 0 28.5-11.5T720-320v-100q0-17-11.5-28.5T680-460H570q-13 0-21.5 8.5T540-430v180q0 13 8.5 21.5T570-220q13 0 21.5-8.5T600-250v-30Zm0-60v-60h60v60h-60Z"/></svg>

After

Width:  |  Height:  |  Size: 968 B

+1
View File
@@ -0,0 +1 @@
<svg fill="currentColor" xmlns="http://www.w3.org/2000/svg" viewBox="0 -960 960 960"><path d="M340-680v150q0 13 8.5 21.5T370-500q13 0 21.5-8.5T400-530v-180q0-13-8.5-21.5T370-740h-60q-13 0-21.5 8.5T280-710q0 13 8.5 21.5T310-680h30Zm180 180h100q17 0 28.5-11.5T660-540v-60q0-17-11.5-28.5T620-640h-80v-40h90q13 0 21.5-8.5T660-710q0-13-8.5-21.5T630-740H520q-17 0-28.5 11.5T480-700v160q0 17 11.5 28.5T520-500Zm20-40v-60h60v60h-60ZM200-120q-33 0-56.5-23.5T120-200v-560q0-33 23.5-56.5T200-840h560q33 0 56.5 23.5T840-760v560q0 33-23.5 56.5T760-120H200Zm100-280h40v90q0 13 8.5 21.5T370-280q13 0 21.5-8.5T400-310v-90h40v150q0 13 8.5 21.5T470-220q13 0 21.5-8.5T500-250v-170q0-17-11.5-28.5T460-460H280q-17 0-28.5 11.5T240-420v170q0 13 8.5 21.5T270-220q13 0 21.5-8.5T300-250v-150Zm300 120h80q17 0 28.5-11.5T720-320v-100q0-17-11.5-28.5T680-460H570q-13 0-21.5 8.5T540-430v180q0 13 8.5 21.5T570-220q13 0 21.5-8.5T600-250v-30Zm0-60v-60h60v60h-60Z"/></svg>

After

Width:  |  Height:  |  Size: 937 B

+1
View File
@@ -0,0 +1 @@
<svg fill="currentColor" xmlns="http://www.w3.org/2000/svg" viewBox="0 -960 960 960"><path d="M340-680v150q0 13 8.5 21.5T370-500q13 0 21.5-8.5T400-530v-180q0-13-8.5-21.5T370-740h-60q-13 0-21.5 8.5T280-710q0 13 8.5 21.5T310-680h30Zm244 0-41 137q-5 16 5 29.5t27 13.5q11 0 20-6.5t12-17.5l51-164q6-20-6-36t-32-16H510q-13 0-21.5 8.5T480-710q0 13 8.5 21.5T510-680h74ZM200-120q-33 0-56.5-23.5T120-200v-560q0-33 23.5-56.5T200-840h560q33 0 56.5 23.5T840-760v560q0 33-23.5 56.5T760-120H200Zm100-280h40v90q0 13 8.5 21.5T370-280q13 0 21.5-8.5T400-310v-90h40v150q0 13 8.5 21.5T470-220q13 0 21.5-8.5T500-250v-170q0-17-11.5-28.5T460-460H280q-17 0-28.5 11.5T240-420v170q0 13 8.5 21.5T270-220q13 0 21.5-8.5T300-250v-150Zm300 120h80q17 0 28.5-11.5T720-320v-100q0-17-11.5-28.5T680-460H570q-13 0-21.5 8.5T540-430v180q0 13 8.5 21.5T570-220q13 0 21.5-8.5T600-250v-30Zm0-60v-60h60v60h-60Z"/></svg>

After

Width:  |  Height:  |  Size: 874 B

@@ -0,0 +1 @@
<svg fill="currentColor" xmlns="http://www.w3.org/2000/svg" viewBox="0 -960 960 960"><path d="M340-540v150q0 13 8.5 21.5T370-360q13 0 21.5-8.5T400-390v-180q0-13-8.5-21.5T370-600h-60q-13 0-21.5 8.5T280-570q0 13 8.5 21.5T310-540h30Zm160 180h100q17 0 28.5-11.5T640-400v-160q0-17-11.5-28.5T600-600H500q-17 0-28.5 11.5T460-560v160q0 17 11.5 28.5T500-360Zm20-40v-60h60v60h-60Zm0-100v-60h60v60h-60ZM200-120q-33 0-56.5-23.5T120-200v-560q0-33 23.5-56.5T200-840h560q33 0 56.5 23.5T840-760v560q0 33-23.5 56.5T760-120H200Z"/></svg>

After

Width:  |  Height:  |  Size: 519 B

+1
View File
@@ -0,0 +1 @@
<svg fill="currentColor" xmlns="http://www.w3.org/2000/svg" viewBox="0 -960 960 960"><path d="M340-680v150q0 13 8.5 21.5T370-500q13 0 21.5-8.5T400-530v-180q0-13-8.5-21.5T370-740h-60q-13 0-21.5 8.5T280-710q0 13 8.5 21.5T310-680h30Zm180 180h100q17 0 28.5-11.5T660-540v-160q0-17-11.5-28.5T620-740H520q-17 0-28.5 11.5T480-700v160q0 17 11.5 28.5T520-500Zm20-140v-60h60v60h-60Zm0 100v-60h60v60h-60ZM200-120q-33 0-56.5-23.5T120-200v-560q0-33 23.5-56.5T200-840h560q33 0 56.5 23.5T840-760v560q0 33-23.5 56.5T760-120H200Zm100-280h40v90q0 13 8.5 21.5T370-280q13 0 21.5-8.5T400-310v-90h40v150q0 13 8.5 21.5T470-220q13 0 21.5-8.5T500-250v-170q0-17-11.5-28.5T460-460H280q-17 0-28.5 11.5T240-420v170q0 13 8.5 21.5T270-220q13 0 21.5-8.5T300-250v-150Zm300 120h80q17 0 28.5-11.5T720-320v-100q0-17-11.5-28.5T680-460H570q-13 0-21.5 8.5T540-430v180q0 13 8.5 21.5T570-220q13 0 21.5-8.5T600-250v-30Zm0-60v-60h60v60h-60Z"/></svg>

After

Width:  |  Height:  |  Size: 905 B

+1
View File
@@ -0,0 +1 @@
<svg fill="currentColor" xmlns="http://www.w3.org/2000/svg" viewBox="0 -960 960 960"><path d="M340-680v150q0 13 8.5 21.5T370-500q13 0 21.5-8.5T400-530v-180q0-13-8.5-21.5T370-740h-60q-13 0-21.5 8.5T280-710q0 13 8.5 21.5T310-680h30Zm260 120h-90q-13 0-21.5 8.5T480-530q0 13 8.5 21.5T510-500h110q17 0 28.5-11.5T660-540v-160q0-17-11.5-28.5T620-740H520q-17 0-28.5 11.5T480-700v60q0 17 11.5 28.5T520-600h80v40Zm-60-80v-60h60v60h-60ZM200-120q-33 0-56.5-23.5T120-200v-560q0-33 23.5-56.5T200-840h560q33 0 56.5 23.5T840-760v560q0 33-23.5 56.5T760-120H200Zm100-280h40v90q0 13 8.5 21.5T370-280q13 0 21.5-8.5T400-310v-90h40v150q0 13 8.5 21.5T470-220q13 0 21.5-8.5T500-250v-170q0-17-11.5-28.5T460-460H280q-17 0-28.5 11.5T240-420v170q0 13 8.5 21.5T270-220q13 0 21.5-8.5T300-250v-150Zm300 120h80q17 0 28.5-11.5T720-320v-100q0-17-11.5-28.5T680-460H570q-13 0-21.5 8.5T540-430v180q0 13 8.5 21.5T570-220q13 0 21.5-8.5T600-250v-30Zm0-60v-60h60v60h-60Z"/></svg>

After

Width:  |  Height:  |  Size: 938 B

+1
View File
@@ -0,0 +1 @@
<svg fill="currentColor" xmlns="http://www.w3.org/2000/svg" viewBox="0 -960 960 960"><path d="m540-450 62 79q2 3 22 11 17 0 25-15.5t-3-29.5l-56-75 57-76q11-14 3-29t-26-15q-7 0-12.5 3t-9.5 8l-62 79v-60q0-13-8.5-21.5T510-600q-13 0-21.5 8.5T480-570v180q0 13 8.5 21.5T510-360q13 0 21.5-8.5T540-390v-60Zm-200-90v150q0 13 8.5 21.5T370-360q13 0 21.5-8.5T400-390v-180q0-13-8.5-21.5T370-600h-60q-13 0-21.5 8.5T280-570q0 13 8.5 21.5T310-540h30ZM200-120q-33 0-56.5-23.5T120-200v-560q0-33 23.5-56.5T200-840h560q33 0 56.5 23.5T840-760v560q0 33-23.5 56.5T760-120H200Z"/></svg>

After

Width:  |  Height:  |  Size: 562 B

+1
View File
@@ -0,0 +1 @@
<svg fill="currentColor" xmlns="http://www.w3.org/2000/svg" viewBox="0 -960 960 960"><path d="M660-460v40q0 8 6 14t14 6q8 0 14-6t6-14v-40h40q8 0 14-6t6-14q0-8-6-14t-14-6h-40v-40q0-8-6-14t-14-6q-8 0-14 6t-6 14v40h-40q-8 0-14 6t-6 14q0 8 6 14t14 6h40Zm-180 10 62 79q2 3 22 11 17 0 25-15.5t-3-29.5l-56-75 57-76q11-14 3-29t-26-15q-7 0-12.5 3t-9.5 8l-62 79v-60q0-13-8.5-21.5T450-600q-13 0-21.5 8.5T420-570v180q0 13 8.5 21.5T450-360q13 0 21.5-8.5T480-390v-60Zm-180-90v150q0 13 8.5 21.5T330-360q13 0 21.5-8.5T360-390v-180q0-13-8.5-21.5T330-600h-60q-13 0-21.5 8.5T240-570q0 13 8.5 21.5T270-540h30ZM200-120q-33 0-56.5-23.5T120-200v-560q0-33 23.5-56.5T200-840h560q33 0 56.5 23.5T840-760v560q0 33-23.5 56.5T760-120H200Z"/></svg>

After

Width:  |  Height:  |  Size: 717 B

@@ -0,0 +1 @@
<svg fill="currentColor" xmlns="http://www.w3.org/2000/svg" viewBox="0 -960 960 960"><path d="M240-600h-40q-17 0-28.5-11.5T160-640q0-17 11.5-28.5T200-680h80q17 0 28.5 11.5T320-640v320q0 17-11.5 28.5T280-280q-17 0-28.5-11.5T240-320v-280Zm346 186-69 115q-5 9-13.5 14t-19.5 5q-23 0-34.5-20t.5-40l90-152-77-127q-12-20-.5-40.5T497-680q11 0 20.5 5.5T532-660l54 90 56-91q5-9 14-14t20-5q23 0 34.5 20t-.5 40l-76 128 90 151q12 20 .5 40.5T689-280q-11 0-20.5-5.5T654-300l-68-114Z"/></svg>

After

Width:  |  Height:  |  Size: 476 B

@@ -0,0 +1 @@
<svg fill="currentColor" xmlns="http://www.w3.org/2000/svg" viewBox="0 -960 960 960"><path d="M120-120q-33 0-56.5-23.5T40-200v-560q0-33 23.5-56.5T120-840h720q33 0 56.5 23.5T920-760v560q0 33-23.5 56.5T840-120H120Zm160-480v280q0 17 11.5 28.5T320-280q17 0 28.5-11.5T360-320v-320q0-17-11.5-28.5T320-680h-80q-17 0-28.5 11.5T200-640q0 17 11.5 28.5T240-600h40Zm330 193 60 109q5 8 13 13t18 5q20 0 30-17.5t0-34.5l-81-148 82-148q10-17-.5-34.5T701-680q-10 0-18 5t-13 13l-60 109-60-109q-5-8-13-13t-18-5q-20 0-30 17.5t0 34.5l81 148-82 148q-10 17 .5 34.5T519-280q10 0 18-5t13-13l60-109Z"/></svg>

After

Width:  |  Height:  |  Size: 581 B

+1
View File
@@ -0,0 +1 @@
<svg fill="currentColor" xmlns="http://www.w3.org/2000/svg" viewBox="0 -960 960 960"><path d="M320-560v-40h80q17 0 28.5-11.5T440-640v-60q0-17-11.5-28.5T400-740H290q-13 0-21.5 8.5T260-710q0 13 8.5 21.5T290-680h90v40h-80q-17 0-28.5 11.5T260-600v70q0 13 8.5 21.5T290-500h120q13 0 21.5-8.5T440-530q0-13-8.5-21.5T410-560h-90Zm240 60h100q17 0 28.5-11.5T700-540v-160q0-17-11.5-28.5T660-740H560q-17 0-28.5 11.5T520-700v160q0 17 11.5 28.5T560-500Zm20-60v-120h60v120h-60ZM200-120q-33 0-56.5-23.5T120-200v-560q0-33 23.5-56.5T200-840h560q33 0 56.5 23.5T840-760v560q0 33-23.5 56.5T760-120H200Zm100-280h40v90q0 13 8.5 21.5T370-280q13 0 21.5-8.5T400-310v-90h40v150q0 13 8.5 21.5T470-220q13 0 21.5-8.5T500-250v-170q0-17-11.5-28.5T460-460H280q-17 0-28.5 11.5T240-420v170q0 13 8.5 21.5T270-220q13 0 21.5-8.5T300-250v-150Zm300 120h80q17 0 28.5-11.5T720-320v-100q0-17-11.5-28.5T680-460H570q-13 0-21.5 8.5T540-430v180q0 13 8.5 21.5T570-220q13 0 21.5-8.5T600-250v-30Zm0-60v-60h60v60h-60Z"/></svg>

After

Width:  |  Height:  |  Size: 974 B

+1
View File
@@ -0,0 +1 @@
<svg fill="currentColor" xmlns="http://www.w3.org/2000/svg" viewBox="0 -960 960 960"><path d="M360-560v-40h80q17 0 28.5-11.5T480-640v-60q0-17-11.5-28.5T440-740H330q-13 0-21.5 8.5T300-710q0 13 8.5 21.5T330-680h90v40h-80q-17 0-28.5 11.5T300-600v70q0 13 8.5 21.5T330-500h120q13 0 21.5-8.5T480-530q0-13-8.5-21.5T450-560h-90Zm220-120v150q0 13 8.5 21.5T610-500q13 0 21.5-8.5T640-530v-180q0-13-8.5-21.5T610-740h-60q-13 0-21.5 8.5T520-710q0 13 8.5 21.5T550-680h30ZM200-120q-33 0-56.5-23.5T120-200v-560q0-33 23.5-56.5T200-840h560q33 0 56.5 23.5T840-760v560q0 33-23.5 56.5T760-120H200Zm100-280h40v90q0 13 8.5 21.5T370-280q13 0 21.5-8.5T400-310v-90h40v150q0 13 8.5 21.5T470-220q13 0 21.5-8.5T500-250v-170q0-17-11.5-28.5T460-460H280q-17 0-28.5 11.5T240-420v170q0 13 8.5 21.5T270-220q13 0 21.5-8.5T300-250v-150Zm300 120h80q17 0 28.5-11.5T720-320v-100q0-17-11.5-28.5T680-460H570q-13 0-21.5 8.5T540-430v180q0 13 8.5 21.5T570-220q13 0 21.5-8.5T600-250v-30Zm0-60v-60h60v60h-60Z"/></svg>

After

Width:  |  Height:  |  Size: 969 B

+1
View File
@@ -0,0 +1 @@
<svg fill="currentColor" xmlns="http://www.w3.org/2000/svg" viewBox="0 -960 960 960"><path d="M320-560v-40h80q17 0 28.5-11.5T440-640v-60q0-17-11.5-28.5T400-740H290q-13 0-21.5 8.5T260-710q0 13 8.5 21.5T290-680h90v40h-80q-17 0-28.5 11.5T260-600v70q0 13 8.5 21.5T290-500h120q13 0 21.5-8.5T440-530q0-13-8.5-21.5T410-560h-90Zm260 0v-40h80q17 0 28.5-11.5T700-640v-60q0-17-11.5-28.5T660-740H550q-13 0-21.5 8.5T520-710q0 13 8.5 21.5T550-680h90v40h-80q-17 0-28.5 11.5T520-600v70q0 13 8.5 21.5T550-500h120q13 0 21.5-8.5T700-530q0-13-8.5-21.5T670-560h-90ZM200-120q-33 0-56.5-23.5T120-200v-560q0-33 23.5-56.5T200-840h560q33 0 56.5 23.5T840-760v560q0 33-23.5 56.5T760-120H200Zm100-280h40v90q0 13 8.5 21.5T370-280q13 0 21.5-8.5T400-310v-90h40v150q0 13 8.5 21.5T470-220q13 0 21.5-8.5T500-250v-170q0-17-11.5-28.5T460-460H280q-17 0-28.5 11.5T240-420v170q0 13 8.5 21.5T270-220q13 0 21.5-8.5T300-250v-150Zm300 120h80q17 0 28.5-11.5T720-320v-100q0-17-11.5-28.5T680-460H570q-13 0-21.5 8.5T540-430v180q0 13 8.5 21.5T570-220q13 0 21.5-8.5T600-250v-30Zm0-60v-60h60v60h-60Z"/></svg>

After

Width:  |  Height:  |  Size: 1.0 KiB

+1
View File
@@ -0,0 +1 @@
<svg fill="currentColor" xmlns="http://www.w3.org/2000/svg" viewBox="0 -960 960 960"><path d="M320-560v-40h80q17 0 28.5-11.5T440-640v-60q0-17-11.5-28.5T400-740H290q-13 0-21.5 8.5T260-710q0 13 8.5 21.5T290-680h90v40h-80q-17 0-28.5 11.5T260-600v70q0 13 8.5 21.5T290-500h120q13 0 21.5-8.5T440-530q0-13-8.5-21.5T410-560h-90Zm320 0h-90q-13 0-21.5 8.5T520-530q0 13 8.5 21.5T550-500h110q17 0 28.5-11.5T700-540v-160q0-17-11.5-28.5T660-740H550q-13 0-21.5 8.5T520-710q0 13 8.5 21.5T550-680h90v40h-60q-8 0-14 6t-6 14q0 8 6 14t14 6h60v40ZM200-120q-33 0-56.5-23.5T120-200v-560q0-33 23.5-56.5T200-840h560q33 0 56.5 23.5T840-760v560q0 33-23.5 56.5T760-120H200Zm100-280h40v90q0 13 8.5 21.5T370-280q13 0 21.5-8.5T400-310v-90h40v150q0 13 8.5 21.5T470-220q13 0 21.5-8.5T500-250v-170q0-17-11.5-28.5T460-460H280q-17 0-28.5 11.5T240-420v170q0 13 8.5 21.5T270-220q13 0 21.5-8.5T300-250v-150Zm300 120h80q17 0 28.5-11.5T720-320v-100q0-17-11.5-28.5T680-460H570q-13 0-21.5 8.5T540-430v180q0 13 8.5 21.5T570-220q13 0 21.5-8.5T600-250v-30Zm0-60v-60h60v60h-60Z"/></svg>

After

Width:  |  Height:  |  Size: 1.0 KiB

@@ -0,0 +1 @@
<svg fill="currentColor" xmlns="http://www.w3.org/2000/svg" viewBox="0 -960 960 960"><path d="M720-400q-17 0-28.5-11.5T680-440v-80H560q-17 0-28.5-11.5T520-560v-200q0-17 11.5-28.5T560-800q17 0 28.5 11.5T600-760v160h80v-160q0-17 11.5-28.5T720-800q17 0 28.5 11.5T760-760v160h40q17 0 28.5 11.5T840-560q0 17-11.5 28.5T800-520h-40v80q0 17-11.5 28.5T720-400Zm-480 0q-17 0-28.5-11.5T200-440v-120q0-33 23.5-56.5T280-640h80v-80H240q-17 0-28.5-11.5T200-760q0-17 11.5-28.5T240-800h120q33 0 56.5 23.5T440-720v80q0 33-23.5 56.5T360-560h-80v80h120q17 0 28.5 11.5T440-440q0 17-11.5 28.5T400-400H240ZM160-80q-17 0-28.5-11.5T120-120v-120q0-17 11.5-28.5T160-280q17 0 28.5 11.5T200-240v120q0 17-11.5 28.5T160-80Zm160 0q-17 0-28.5-11.5T280-120v-120q0-17 11.5-28.5T320-280q17 0 28.5 11.5T360-240v120q0 17-11.5 28.5T320-80Zm160 0q-17 0-28.5-11.5T440-120v-120q0-17 11.5-28.5T480-280q17 0 28.5 11.5T520-240v120q0 17-11.5 28.5T480-80Zm160 0q-17 0-28.5-11.5T600-120v-120q0-17 11.5-28.5T640-280h160q17 0 28.5 11.5T840-240v120q0 17-11.5 28.5T800-80H640Z"/></svg>

After

Width:  |  Height:  |  Size: 1.0 KiB

+1
View File
@@ -0,0 +1 @@
<svg fill="currentColor" xmlns="http://www.w3.org/2000/svg" viewBox="0 -960 960 960"><path d="M320-560v-40h80q17 0 28.5-11.5T440-640v-60q0-17-11.5-28.5T400-740H290q-13 0-21.5 8.5T260-710q0 13 8.5 21.5T290-680h90v40h-80q-17 0-28.5 11.5T260-600v70q0 13 8.5 21.5T290-500h120q13 0 21.5-8.5T440-530q0-13-8.5-21.5T410-560h-90Zm320 0v30q0 13 8.5 21.5T670-500q13 0 21.5-8.5T700-530v-30h10q13 0 21.5-8.5T740-590q0-13-8.5-21.5T710-620h-10v-90q0-13-8.5-21.5T670-740q-13 0-21.5 8.5T640-710v90h-60v-90q0-13-8.5-21.5T550-740q-13 0-21.5 8.5T520-710v120q0 13 8.5 21.5T550-560h90ZM200-120q-33 0-56.5-23.5T120-200v-560q0-33 23.5-56.5T200-840h560q33 0 56.5 23.5T840-760v560q0 33-23.5 56.5T760-120H200Zm100-280h40v90q0 13 8.5 21.5T370-280q13 0 21.5-8.5T400-310v-90h40v150q0 13 8.5 21.5T470-220q13 0 21.5-8.5T500-250v-170q0-17-11.5-28.5T460-460H280q-17 0-28.5 11.5T240-420v170q0 13 8.5 21.5T270-220q13 0 21.5-8.5T300-250v-150Zm300 120h80q17 0 28.5-11.5T720-320v-100q0-17-11.5-28.5T680-460H570q-13 0-21.5 8.5T540-430v180q0 13 8.5 21.5T570-220q13 0 21.5-8.5T600-250v-30Zm0-60v-60h60v60h-60Z"/></svg>

After

Width:  |  Height:  |  Size: 1.1 KiB

+1
View File
@@ -0,0 +1 @@
<svg fill="currentColor" xmlns="http://www.w3.org/2000/svg" viewBox="0 -960 960 960"><path d="M320-420v-40h80q17 0 28.5-11.5T440-500v-60q0-17-11.5-28.5T400-600H290q-13 0-21.5 8.5T260-570q0 13 8.5 21.5T290-540h90v40h-80q-17 0-28.5 11.5T260-460v70q0 13 8.5 21.5T290-360h120q13 0 21.5-8.5T440-390q0-13-8.5-21.5T410-420h-90Zm230 60h110q25 0 42.5-17.5T720-420v-120q0-25-17.5-42.5T660-600H550q-13 0-21.5 8.5T520-570v180q0 13 8.5 21.5T550-360Zm30-60v-120h80v120h-80ZM200-120q-33 0-56.5-23.5T120-200v-560q0-33 23.5-56.5T200-840h560q33 0 56.5 23.5T840-760v560q0 33-23.5 56.5T760-120H200Z"/></svg>

After

Width:  |  Height:  |  Size: 587 B

+1
View File
@@ -0,0 +1 @@
<svg fill="currentColor" xmlns="http://www.w3.org/2000/svg" viewBox="0 -960 960 960"><path d="M376-279H172q-11 0-18-7t-7-18v-32q0-5 1.5-10.5t6.5-8.5q51-41 84.5-68.5T292-478q16-22 24-40.5t8-38.5q0-30-17-47.5T266-622q-17 0-29.5 9.5T216-589q-7 13-20.5 18t-26.5-1q-14-7-17-22t3-29q14-32 45.5-49.5T266-690q63 0 97.5 32t34.5 90q0 37-14 69t-36 61q-23 31-46.5 50.5T249-346l2 3h125q14 0 23 9t9 23q0 14-9 23t-23 9Zm152-1q-17 0-28.5-11.5T488-320v-321q0-17 11.5-28.5T528-681h94q97 0 148.5 52T822-480q0 97-52 148.5T622-280h-94Zm36-72h54q66 0 97.5-31t31.5-97q0-66-31.5-97.5T618-609h-54v257Z"/></svg>

After

Width:  |  Height:  |  Size: 585 B

+1
View File
@@ -0,0 +1 @@
<svg fill="currentColor" xmlns="http://www.w3.org/2000/svg" viewBox="0 -960 960 960"><path d="m580-450 62 79q2 3 22 11 17 0 25-15.5t-3-29.5l-56-75 57-76q11-14 3-29t-26-15q-7 0-12.5 3t-9.5 8l-62 79v-60q0-13-8.5-21.5T550-600q-13 0-21.5 8.5T520-570v180q0 13 8.5 21.5T550-360q13 0 21.5-8.5T580-390v-60Zm-260 30v-40h80q17 0 28.5-11.5T440-500v-60q0-17-11.5-28.5T400-600H290q-13 0-21.5 8.5T260-570q0 13 8.5 21.5T290-540h90v40h-80q-17 0-28.5 11.5T260-460v70q0 13 8.5 21.5T290-360h120q13 0 21.5-8.5T440-390q0-13-8.5-21.5T410-420h-90ZM200-120q-33 0-56.5-23.5T120-200v-560q0-33 23.5-56.5T200-840h560q33 0 56.5 23.5T840-760v560q0 33-23.5 56.5T760-120H200Z"/></svg>

After

Width:  |  Height:  |  Size: 652 B

+1
View File
@@ -0,0 +1 @@
<svg fill="currentColor" xmlns="http://www.w3.org/2000/svg" viewBox="0 -960 960 960"><path d="M660-460v40q0 8 6 14t14 6q8 0 14-6t6-14v-40h40q8 0 14-6t6-14q0-8-6-14t-14-6h-40v-40q0-8-6-14t-14-6q-8 0-14 6t-6 14v40h-40q-8 0-14 6t-6 14q0 8 6 14t14 6h40Zm-160 10 62 79q2 3 22 11 17 0 25-15.5t-3-29.5l-56-75 57-76q11-14 3-29t-26-15q-7 0-12.5 3t-9.5 8l-62 79v-60q0-13-8.5-21.5T470-600q-13 0-21.5 8.5T440-570v180q0 13 8.5 21.5T470-360q13 0 21.5-8.5T500-390v-60Zm-200 30v-40h60q17 0 28.5-11.5T400-500v-60q0-17-11.5-28.5T360-600h-90q-13 0-21.5 8.5T240-570q0 13 8.5 21.5T270-540h70v40h-60q-17 0-28.5 11.5T240-460v70q0 13 8.5 21.5T270-360h100q13 0 21.5-8.5T400-390q0-13-8.5-21.5T370-420h-70ZM200-120q-33 0-56.5-23.5T120-200v-560q0-33 23.5-56.5T200-840h560q33 0 56.5 23.5T840-760v560q0 33-23.5 56.5T760-120H200Z"/></svg>

After

Width:  |  Height:  |  Size: 807 B

+1
View File
@@ -0,0 +1 @@
<svg fill="currentColor" xmlns="http://www.w3.org/2000/svg" viewBox="0 -960 960 960"><path d="M300-400h40v90q0 13 8.5 21.5T370-280q13 0 21.5-8.5T400-310v-90h40v150q0 13 8.5 21.5T470-220q13 0 21.5-8.5T500-250v-170q0-17-11.5-28.5T460-460H280q-17 0-28.5 11.5T240-420v170q0 13 8.5 21.5T270-220q13 0 21.5-8.5T300-250v-150Zm150-160v-40h80q17 0 28.5-11.5T570-640v-60q0-17-11.5-28.5T530-740H420q-13 0-21.5 8.5T390-710q0 13 8.5 21.5T420-680h90v40h-80q-17 0-28.5 11.5T390-600v70q0 13 8.5 21.5T420-500h120q13 0 21.5-8.5T570-530q0-13-8.5-21.5T540-560h-90Zm150 280h80q17 0 28.5-11.5T720-320v-100q0-17-11.5-28.5T680-460H570q-13 0-21.5 8.5T540-430v180q0 13 8.5 21.5T570-220q13 0 21.5-8.5T600-250v-30Zm0-60v-60h60v60h-60ZM200-120q-33 0-56.5-23.5T120-200v-560q0-33 23.5-56.5T200-840h560q33 0 56.5 23.5T840-760v560q0 33-23.5 56.5T760-120H200Z"/></svg>

After

Width:  |  Height:  |  Size: 833 B

+1
View File
@@ -0,0 +1 @@
<svg fill="currentColor" xmlns="http://www.w3.org/2000/svg" viewBox="0 -960 960 960"><path d="M320-200H140q-25 0-42.5-17.5T80-260q0-25 17.5-42.5T140-320h180v-100H140q-25 0-42.5-17.5T80-480q0-25 17.5-42.5T140-540h180v-100H140q-25 0-42.5-17.5T80-700q0-25 17.5-42.5T140-760h180q50 0 85 35t35 85v76q0 35-24.5 59.5T356-480q35 0 59.5 24.5T440-396v76q0 50-35 85t-85 35Zm280-120h160v-320H600v320Zm0 120q-50 0-85-35t-35-85v-320q0-50 35-85t85-35h160q50 0 85 35t35 85v320q0 50-35 85t-85 35H600Z"/></svg>

After

Width:  |  Height:  |  Size: 492 B

@@ -0,0 +1 @@
<svg fill="currentColor" xmlns="http://www.w3.org/2000/svg" viewBox="0 -960 960 960"><path d="M200-400q-17 0-28.5-11.5T160-440q0-17 11.5-28.5T200-480h160v-80H200q-17 0-28.5-11.5T160-600q0-17 11.5-28.5T200-640h160v-80H200q-17 0-28.5-11.5T160-760q0-17 11.5-28.5T200-800h160q33 0 56.5 23.5T440-720v60q0 26-17 43t-43 17q26 0 43 17t17 43v60q0 33-23.5 56.5T360-400H200Zm400 0q-33 0-56.5-23.5T520-480v-240q0-33 23.5-56.5T600-800h120q33 0 56.5 23.5T800-720v240q0 33-23.5 56.5T720-400H600Zm0-80h120v-240H600v240ZM160-80q-17 0-28.5-11.5T120-120v-120q0-17 11.5-28.5T160-280q17 0 28.5 11.5T200-240v120q0 17-11.5 28.5T160-80Zm160 0q-17 0-28.5-11.5T280-120v-120q0-17 11.5-28.5T320-280q17 0 28.5 11.5T360-240v120q0 17-11.5 28.5T320-80Zm160 0q-17 0-28.5-11.5T440-120v-120q0-17 11.5-28.5T480-280q17 0 28.5 11.5T520-240v120q0 17-11.5 28.5T480-80Zm160 0q-17 0-28.5-11.5T600-120v-120q0-17 11.5-28.5T640-280h160q17 0 28.5 11.5T840-240v120q0 17-11.5 28.5T800-80H640Z"/></svg>

After

Width:  |  Height:  |  Size: 953 B

+1
View File
@@ -0,0 +1 @@
<svg fill="currentColor" xmlns="http://www.w3.org/2000/svg" viewBox="0 -960 960 960"><path d="M374-288q-128-17-211-70T80-480q0-83 115.5-141.5T480-680q169 0 284.5 58.5T880-480q0 56-54.5 101.5T681-307q-16 5-28.5-4.5T640-337q0-18 10.5-32t27.5-20q60-20 91-45.5t31-45.5q0-32-85.5-76T480-600q-149 0-234.5 44T160-480q0 24 51 57.5T356-372l-24-24q-11-11-11-28t11-28q11-11 28-11t28 11l104 104q12 12 12 28t-12 28L388-188q-11 11-27.5 11.5T332-188q-11-11-11.5-27.5T331-244l43-44Z"/></svg>

After

Width:  |  Height:  |  Size: 475 B

+1
View File
@@ -0,0 +1 @@
<svg fill="currentColor" xmlns="http://www.w3.org/2000/svg" viewBox="0 -960 960 960"><path d="M520-360h120q33 0 56.5-23.5T720-440v-80q0-33-23.5-56.5T640-600H520v240Zm60-60v-120h60q8 0 14 6t6 14v80q0 8-6 14t-14 6h-60Zm-180 60q17 0 28.5-11.5T440-400v-40q0-17-11.5-28.5T400-480q17 0 28.5-11.5T440-520v-40q0-17-11.5-28.5T400-600H290q-13 0-21.5 8.5T260-570q0 13 8.5 21.5T290-540h90v30h-50q-13 0-21.5 8.5T300-480q0 13 8.5 21.5T330-450h50v30h-90q-13 0-21.5 8.5T260-390q0 13 8.5 21.5T290-360h110ZM160-160q-33 0-56.5-23.5T80-240v-480q0-33 23.5-56.5T160-800h640q33 0 56.5 23.5T880-720v480q0 33-23.5 56.5T800-160H160Z"/></svg>

After

Width:  |  Height:  |  Size: 615 B

+1
View File
@@ -0,0 +1 @@
<svg fill="currentColor" xmlns="http://www.w3.org/2000/svg" viewBox="0 -960 960 960"><path d="M530-279q-17 0-28.5-11.5T490-319v-321q0-17 11.5-28.5T530-680h93q97 0 148.5 51.5T823-480q0 97-52 149t-148 52h-93Zm35-72h55q66 0 97-31.5t31-97.5q0-66-31-97.5T620-609h-55v258Zm-289 81q-45 0-78-17.5T149-340q-7-14-1-29t20-22q14-7 28.5-1.5T219-373q8 16 22.5 24.5T276-340q28 0 45-15.5t17-42.5q0-26-18-43t-51-17h-2q-14 0-23-9t-9-23q0-14 9-23t23-9q30 0 45-14t15-37q0-23-14.5-36.5T274-623q-18 0-31 8t-21 22q-7 14-21 18t-28-3q-14-7-18-21t3-28q6-12 14.5-21.5T192-666q16-11 37-17t46-6q55 0 89.5 27.5T399-581q0 30-13.5 50.5T350-496v4q30 11 46 39t16 61q0 59-37.5 90.5T276-270Z"/></svg>

After

Width:  |  Height:  |  Size: 664 B

@@ -0,0 +1 @@
<svg fill="currentColor" xmlns="http://www.w3.org/2000/svg" viewBox="0 -960 960 960"><path d="M620-360H520q-8 0-14-6t-6-14v-200q0-8 6-14t14-6h100q17 0 28.5 11.5T660-560v160q0 17-11.5 28.5T620-360Zm-200 0h-90q-13 0-21.5-8.5T300-390q0-13 8.5-21.5T330-420h70v-40h-40q-8 0-14-6t-6-14q0-8 6-14t14-6h40v-40h-70q-13 0-21.5-8.5T300-570q0-13 8.5-21.5T330-600h90q17 0 28.5 11.5T460-560v160q0 17-11.5 28.5T420-360Zm140-60h40v-120h-40v120Zm280-20q-17 0-28.5-11.5T800-480q0-115-72.5-203T542-794l30 30q11 11 11 28t-11 28q-11 11-28 11t-28-11L387-837q-8-8-4.5-20t14.5-15q20-5 41-6.5t42-1.5q83 0 156 31.5T763-763q54 54 85.5 127T880-480q0 17-11.5 28.5T840-440ZM480-80q-83 0-156-31.5T197-197q-54-54-85.5-127T80-480q0-17 11.5-28.5T120-520q17 0 28.5 11.5T160-480q0 115 72.5 203T418-166l-30-30q-11-11-11-28t11-28q11-11 28-11t28 11l129 129q8 8 4.5 20T563-88q-20 5-41 6.5T480-80Z"/></svg>

After

Width:  |  Height:  |  Size: 864 B

@@ -0,0 +1 @@
<svg fill="currentColor" xmlns="http://www.w3.org/2000/svg" viewBox="0 -960 960 960"><path d="M160-280q-17 0-28.5-11.5T120-320q0-17 11.5-28.5T160-360h160v-80H160q-17 0-28.5-11.5T120-480q0-17 11.5-28.5T160-520h160v-80H160q-17 0-28.5-11.5T120-640q0-17 11.5-28.5T160-680h160q33 0 56.5 23.5T400-600v60q0 25-17.5 42.5T340-480q25 0 42.5 17.5T400-420v60q0 33-23.5 56.5T320-280H160Zm400 0q-33 0-56.5-23.5T480-360v-240q0-33 23.5-56.5T560-680h240q17 0 28.5 11.5T840-640q0 17-11.5 28.5T800-600H560v240h200v-80h-60q-17 0-28.5-11.5T660-480q0-17 11.5-28.5T700-520h100q17 0 28.5 11.5T840-480v120q0 33-23.5 56.5T760-280H560Z"/></svg>

After

Width:  |  Height:  |  Size: 617 B

@@ -0,0 +1 @@
<svg fill="currentColor" xmlns="http://www.w3.org/2000/svg" viewBox="0 -960 960 960"><path d="M120-120q-33 0-56.5-23.5T40-200v-560q0-33 23.5-56.5T120-840h720q33 0 56.5 23.5T920-760v560q0 33-23.5 56.5T840-120H120Zm440-160h120q33 0 56.5-23.5T760-360v-120q0-17-11.5-28.5T720-520h-40q-17 0-28.5 11.5T640-480q0 17 11.5 28.5T680-440v80H560v-240h200q0-33-23.5-56.5T680-680H560q-33 0-56.5 23.5T480-600v240q0 33 23.5 56.5T560-280Zm-200 0q33 0 56.5-23.5T440-360v-60q0-25-17.5-42.5T380-480q25 0 42.5-17.5T440-540v-60q0-33-23.5-56.5T360-680H240q-17 0-28.5 11.5T200-640q0 17 11.5 28.5T240-600h120v80H240q-17 0-28.5 11.5T200-480q0 17 11.5 28.5T240-440h120v80H240q-17 0-28.5 11.5T200-320q0 17 11.5 28.5T240-280h120Z"/></svg>

After

Width:  |  Height:  |  Size: 709 B

Some files were not shown because too many files have changed in this diff Show More