AdminX storybook updates (#18503)

refs. https://github.com/TryGhost/Product/issues/3949
This commit is contained in:
Peter Zimon 2023-10-09 17:04:01 +02:00 committed by GitHub
parent 9abd466397
commit e6f4144909
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
19 changed files with 441 additions and 163 deletions

View File

@ -0,0 +1,38 @@
import {create} from '@storybook/theming/create';
export default create({
base: 'light',
// Typography
fontBase: '"Inter", sans-serif',
fontCode: 'monospace',
brandTitle: 'AdminX Design System',
brandUrl: 'https://ghost.org',
brandImage: 'https://github.com/peterzimon/playground/assets/353959/c4358b4e-232f-4dba-8abb-adb3142ccd89',
brandTarget: '_self',
//
colorPrimary: '#30CF43',
colorSecondary: '#15171A',
// UI
appBg: '#ffffff',
appContentBg: '#ffffff',
appBorderColor: '#EBEEF0',
appBorderRadius: 0,
// Text colors
textColor: '#15171A',
textInverseColor: '#ffffff',
// Toolbar default and active colors
barTextColor: '#9E9E9E',
barSelectedColor: '#15171A',
barBg: '#ffffff',
// Form colors
inputBg: '#ffffff',
inputBorder: '#15171A',
inputTextColor: '#15171A',
inputBorderRadius: 2,
});

View File

@ -0,0 +1,6 @@
import {addons} from '@storybook/manager-api';
import adminxTheme from './adminx-theme';
addons.setConfig({
theme: adminxTheme
});

View File

@ -4,6 +4,10 @@ import '../src/styles/demo.css';
import type { Preview } from "@storybook/react";
import '../src/admin-x-ds/providers/DesignSystemProvider';
import DesignSystemProvider from '../src/admin-x-ds/providers/DesignSystemProvider';
import adminxTheme from './adminx-theme';
import { themes } from '@storybook/theming';
import '../src/admin-x-ds/assets/styles/storybook.css';
const preview: Preview = {
parameters: {
@ -17,9 +21,12 @@ const preview: Preview = {
options: {
storySort: {
mathod: 'alphabetical',
order: ['Global', ['Chrome', 'Form', 'Modal', 'Layout', 'List', '*'], 'Settings', ['Setting Section', 'Setting Group', '*'], 'Experimental'],
order: ['Welcome', 'Foundations', ['Style Guide', 'Colors', 'Icons', 'ErrorHandling'], 'Global', ['Form', 'Chrome', 'Modal', 'Layout', 'List', 'Table', '*'], 'Settings', ['Setting Section', 'Setting Group', '*'], 'Experimental'],
},
},
docs: {
theme: adminxTheme,
},
},
decorators: [
(Story, context) => {

View File

@ -0,0 +1,214 @@
.sbdocs-wrapper {
padding: 3vmin !important;
}
.sbdocs-wrapper .sbdocs-content {
max-width: 1320px;
}
.sb-doc {
max-width: 740px;
width: 100%;
margin: 0 auto !important;
}
.sb-doc,
.sb-doc a,
.sb-doc h1,
.sb-doc h2,
.sb-doc h3,
.sb-doc h4,
.sb-doc h5,
.sb-doc h6,
.sb-doc p,
.sb-doc ul li,
.sbdocs-title,
.sb-doc ol li {
font-family: Inter, sans-serif !important;
padding: 0 !important;
}
.sb-doc a {
color: #30CF43;
}
.sb-doc h1 {
font-size: 48px !important;
letter-spacing: -0.04em !important;
margin-bottom: 20px;
}
.sb-doc h2 {
margin-top: 40px !important;
font-size: 27px;
border: none;
margin-bottom: 2px;
}
.sb-doc h3 {
margin-top: 40px !important;
margin-bottom: 4px !important;
font-size: 20px;
}
.sb-doc h4 {
margin: 0 0 4px !important;
}
.sb-doc p,
.sb-doc div,
.sb-doc ul li,
.sb-doc ol li {
font-size: 15px;
line-height: 1.5em;
}
.sb-doc ul li,
.sb-doc ol li {
margin-bottom: 8px;
}
.sb-doc h2 + p,
.sb-doc h3 + p {
margin-top: 8px;
}
.sb-doc img,
.sb-wide img {
margin-top: 40px !important;
margin-bottom: 40px !important;
}
.sb-doc img.small {
max-width: 520px;
margin: 0 auto;
display: block;
}
.sb-doc p.excerpt {
font-size: 19px;
letter-spacing: -0.02em;
}
.sb-doc .highlight {
padding: 12px 20px;
border-radius: 4px;
background: #EBEEF0;
}
.sb-doc .highlight.purple {
background: #F0E9FA;
}
.sb-doc .highlight.purple a {
color: #8E42FF;
}
/* Welcome */
.sb-doc img.main-image {
margin-top: -2vmin !important;
margin-left: -44px;
margin-right: -32px;
margin-bottom: 0 !important;
max-width: unset;
width: calc(100% + 64px);
}
.sb-doc .main-structure-container {
display: flex;
gap: 32px;
margin: 32px 0 80px;
}
.sb-doc .main-structure-container div {
flex-basis: 33%;
}
.sb-doc .main-structure-container div p {
display: flex;
flex-direction: column;
gap: 4px;
}
.sb-doc .main-structure-container img {
margin: 12px 0 !important;
width: 32px;
height: 32px;
}
.sb-doc .main-structure-container div h4 {
border-bottom: 1px solid #EBEEF0;
padding-bottom: 8px !important;
margin-bottom: 8px !important;
}
.sb-doc .main-structure-container div p {
margin: 0;
font-size: 13.5px;
}
/* Colors */
.color-grid {
display: flex;
gap: 20px;
flex-wrap: wrap;
margin-top: 20px;
}
.color-grid div {
display: flex;
flex-direction: column;
align-items: center;
gap: 8px;
padding: 12px;
border-radius: 4px;
border: 1px solid #EBEEF0;
}
.color-grid .swatch {
display: block;
background: #EFEFEF;
border-radius: 100%;
width: 28px;
height: 28px;
}
.swatch.green {
background: #30CF43;
}
.swatch.black {
background: #15171A;
}
.swatch.white {
background: #FFFFFF;
border: 1px solid #EBEEF0;
}
.swatch.lime {
background: #B5FF18;
}
.swatch.blue {
background: #14B8FF;
}
.swatch.purple {
background: #8E42FF;
}
.swatch.pink {
background: #FB2D8D;
}
.swatch.yellow {
background: #FFB41F;
}
.swatch.red {
background: #F50B23;
}
/* Icons */
.sb-doc .streamline {
display: grid;
grid-template-columns: auto 240px;
gap: 32px;
}

View File

@ -1,41 +0,0 @@
import { Meta } from '@storybook/blocks';
<Meta title="About" />
<style>
{`
.sb-doc {
max-width: 720px;
width: 100%;
font-family: Inter, sans-serif !important;
margin: 0 auto;
}
.sb-highlight {
background: yellow;
border-radius: 7px;
padding: 12px 24px;
margin: 0;
}
`}
</style>
<div className="sb-doc">
# AdminX Design System
This is a documentation of the design system of Ghost AdminX.
## Why?
The purpose of any design system is to bring order to chaos and this is not any diffrent here either. Historically we maintained consistency in the Ghost Admin via using components and global CSS as much as possible. However due to the growth of the product and the loose system, more and more exotic patterns and styles emerged—which resulted in inconsistency.
The main goal of the design system (any design system) is to bring order into chaos by defining and documenting **reusable components**.
## Install
ATM the design system is part of the AdminX Settings project but soon it will be decoupled with the intention to be able to (re)use it in any future React based Admin projects. Eventually it should be an independent package that could be added to any React app.
`yarn add @tryghost/admin-x-ds`
</div>

View File

@ -0,0 +1,56 @@
import { Meta, ColorPalette, ColorItem } from '@storybook/blocks';
<Meta title="Foundations / Colors" />
<div className="sb-doc">
# Colors
<p className='excerpt'>You'll find all the official Ghost colors and more in Tailwind, but here's a list of them in case you want to use them elsewhere.</p>
<ColorPalette>
<ColorItem
title="Primary colors"
colors={{
Green: '#30CF43',
Black: '#15171A',
White: '#FFFFFF'
}}
/>
<ColorItem
title="Secondary colors"
colors={{
Lime: '#B5FF18',
Blue: '#14B8FF',
Purple: '#8E42FF'
}}
/>
<ColorItem
colors={{
Yellow: '#FFB41F',
Red: '#F50B23',
Pink: '#FB2D8D'
}}
/>
<ColorItem
title="Greys"
colors={{
50: '#FAFAFB',
100: '#F4F5F6',
200: '#EBEEF0',
300: '#DDE1E5',
400: '#CED4D9'
}}
/>
<ColorItem
colors={{
500: '#AEB7C1',
600: '#95A1AD',
700: '#7C8B9A',
800: '#626D79',
900: '#394047',
}}
/>
</ColorPalette>
</div>

View File

@ -1,73 +0,0 @@
import { Meta } from '@storybook/blocks';
import SBStructure from './assets/ds-structure.png';
<Meta title="Development" />
<style>
{`
.sb-doc {
max-width: 720px;
width: 100%;
font-family: Inter, sans-serif !important;
margin: 0 auto;
}
.sb-highlight {
background: #E4D2FF;
border-radius: 7px;
padding: 12px 24px;
margin: 0 0 24px;
}
`}
</style>
<div className="sb-doc">
# Development
Here's a guideline of how new components should be developed to match the goal of the design system.
<br />
## Structure
The design system contains **reusable components**. These components live in one of the following groups:
### Global
Contains common components that are used across all parts (in all apps) of the admin: buttons, checkboxes, lists, input fields etc—the usual suspects. You can check them in the "Global" group in the sidebar.
### App specific
The Admin is broken down to multiple React apps, each with its own set custom of reusable components. **These components are also part of the design system** which allows potential reusability in the future, and forces designers and developers to think in terms of the design system.
### What about non-reusable components?
With all fairness, **most of the components should be part of the design system**. Of course there pages, containers etc. in each app which are obviously **non-reusable**, however these should be built up of _mostly_ reusable components.
<p className='sb-highlight'>If you can't decide whether a component is reusable or not, then default to adding it to the design system!</p>
<br />
<img src={SBStructure} />
<br /><br />
### Example: settings app
[TBD: Settings structure]
<br />
## How to develop new components?
If there's a single thing you should remember is this:
<p className='sb-highlight'>Storybook is **the central tool** of the design system, it is **not** an afterthought. It is the **starting point** of the development of any new component.</p>
### Steps to develop new components
[TBD: expand]
1. Create the react component in the desgn system with a rudimentary interface
2. Create a new story (you can use `Boilerplate.stories.tsx` to get started)
3. Develop the details of the new react component
4. Create stories for all interesting use cases. There should be a story for the most common parameter combinations.
5. Test the new component in your app. If something is off, think about how you can solve it in the system—avoid visual hacking in the app.
</div>

View File

@ -3,68 +3,43 @@ import SBLocalError from './assets/local-error-example.png';
import SBPageError from './assets/page-error-example.png';
import SBGlobalError from './assets/global-error-example.png';
<Meta title="Error handling" />
<style>
{`
.sb-doc {
max-width: 720px;
width: 100%;
font-family: Inter, sans-serif !important;
margin: 0 auto;
}
.sb-highlight {
background: #E4D2FF;
border-radius: 7px;
padding: 12px 24px;
margin: 0 0 24px;
}
.sb-wide {
max-width: 1340px;
width: 100%;
margin: 0 auto;
}
`}
</style>
<Meta title="Foundations / Error handling" />
<div className="sb-doc">
# Error handling in AdminX
# Error handling
The three most common errors in Ghost Admin should fall into one of the following categories:
<p className='excerpt'>Consistent error handling throughout the whole product is a key to great user experience.</p>
1. **Inline errors** &mdash; errors related typically to input fields in forms
2. **Contextual errors** &mdash; errors which are related to a screen, page or a modal and the tasks the user is doing at that specific context.
3. **System errors** &mdash; global errors which are relevant no matter what the actual screen is or the user's actual task
Errors in Ghost Admin fall into one of three categories:
## Inline errors
1. **Field errors** &mdash; errors related to various [input] fields in forms
2. **Page errors** &mdash; errors which are related to a screen, page or a modal.
3. **System errors** &mdash; product level, global errors
Inline errors are typically related to form validation and appear most commonly close to a form element. An example of an inline error is when the user doesn't fill a mandatory input field and an error message appears near the field.
## Field errors
Example of a inline error:
Field errors are shown during frontend form validation, and appear close to the related form field. The most typical example of it is when the user doesn't fill a mandatory input field and an error message appears like this:
<img src={SBLocalError} />
<br /><br />
<img src={SBLocalError} className='small' />
Frontend form validation usually happens without an API roundtrip, ideally right after the user finishes editing a field (`onFocusOut`):
1. User fills the form
2. Clicks on the next form field (focus is out of the current input field)
3. The system does form field validation and — in case of an error — shows the related inline field error
All the form fields in AdminX have a built in capability to show inline errors via the `error` prop set to `true` and using the `hint` prop to set the error message. Example: [Textfield](/story/global-form-textfield--error)
This kind of error is usually used for inline frontend form validation, which mostly happens without an API roundtrip. The error is shown right after the user finishes editing the field and focuses out of it (`onBlur`).
Ideally, all validation should be inline: that is, as soon as the user has finished filling in a field, an indicator should appear nearby if the field contains an error. This type of error message is easily noticeable; moreover, fixing the error immediately after the field has been completed requires the least interaction cost for users: they dont need to locate it or navigate to the field, nor do they have to switch context from a new field to return to an old field they thought they had completed successfully.
Of course, there will be situations where inline validation wont be possible and data entered by the user will need to be sent to a server for verification. ([ref](https://www.nngroup.com/articles/errors-forms-design-guidelines/))
Of course, there will be situations where inline validation wont be possible and data entered by the user will need to be sent to a server for verification. ([ref](https://www.nngroup.com/articles/errors-forms-design-guidelines/)).
So the steps to show a field error are:
1. User fills a field in the form
2. Clicks outside the field (`onBlur`)
3. The system does frontend field validation and — in case of an error — shows the related error
All form elements (input fields, selects etc.) in AdminX have a built in capability to show form errors via the `error` prop set to `true` and using the `hint` prop to set the error message. (Example: [Textfield](/story/global-form-textfield--error))
## Page errors
Page errors are related to the actual page, modal or the task but unlike inline errors, they always appear on the top of the page. An important property of page errors is that they _disappear_ if the user navigates away from the page.
A common example of page errors is a summary of form validation errors or an API error or a summary of errors on submitting a form.
Page errors are related to the actual page, modal or the task but unlike inline errors, they always appear on the top of the page. An important property of page errors is that they _disappear_ if the user navigates away from the page. A common example of page errors is a summary of form validation errors or an API error or a summary of errors on submitting a form.
A typical example when page errors would be used:
@ -77,7 +52,6 @@ A typical example when page errors would be used:
<div className="sb-wide">
<img src={SBPageError} />
<br /><br />
</div>
<div className="sb-doc">
@ -90,7 +64,6 @@ For example:
<div className="sb-wide">
<img src={SBGlobalError} />
<br /><br /><br />
</div>

View File

@ -0,0 +1,45 @@
import { Meta } from '@storybook/blocks';
import StreamlineSettings from './assets/streamline-settings.png';
<Meta title="Foundations / Icons" />
<div className="sb-doc">
# Icons
<p className='excerpt'>We use the [Streamline icon library](https://www.streamlinehq.com/) in Ghost Admin. Here are some iconography best practices.</p>
### Overview
The icons are part of the AdminX design system so they can be reusable in any Ghost Admin app. Use the [Icon](/docs/global-icon--docs) component to display available icons in standard icon sizes.
### Adding new icons
There are some rules we have to follow in order to maintain consistent iconography:
<div className="streamline">
<div>
- All icons must be SVG format.
- Icons must be exported using **strokes** as lines and **not** filled areas. The stroke width must be **1.5px**. This allows more flexibility but still maintains consistency (and less filesize).
- All icon should be exported in a 24x24 pixel container.
- All icons should use `currentColor` as their colors for strokes. This lets us color the icon easily in CSS.
- Icons must be exported with transparent background.
<br /><br />
#### Exporting using the Streamline app
For the best result we recommend using the Streamline app to copy the SVG code of new icons with the following settings:
- Format: SVG
- Size: 24px
- Stroke: 1.5px
- Responsive size: OFF
- Outline stroke: **OFF**
- Background: transparent
- Asset currentcolor: **ON**
- Background currentcolor: OFF
</div>
<img src={StreamlineSettings} />
</div>
</div>

View File

@ -0,0 +1,49 @@
import { Meta } from '@storybook/blocks';
import WelcomeImage from './assets/adminx-screenshot.png';
import BlocksIcon from './assets/blocks.svg';
import CircleMenu from './assets/circle-menu.svg';
import AppsIcon from './assets/apps.svg';
<Meta title="Welcome" />
<div className="sb-doc">
<img src={WelcomeImage} className='main-image' />
# AdminX Design System
<p className='excerpt'>Here you can find our design guidelines, component documentation, and resources for building apps in Ghost Admin.</p>
<div className="highlight purple"><strong>This is a work in progress.</strong> As of today, the Storybook system is part of <a href="https://github.com/TryGhost/Ghost/tree/main/apps/admin-x-settings">AdminX Settings app</a> but it's about to be a separated into its own package.</div>
### What's inside
The AdminX Design System is a collection of React UI building blocks for designers and developers to create apps for Ghost Admin. Its main purpose is to provide a library of available components and maintain consistency across the whole product.
<section className="main-structure-container">
<div>
<img src={CircleMenu} className="icon green" />
<h4>Foundations</h4>
<p>Basic global design patterns like colors and icons</p>
[Read more &rarr;](/docs/foundations-colors--docs)
</div>
<div>
<img src={BlocksIcon} className="icon purple" />
<h4>Global components</h4>
<p>UI building blocks to be used across all apps in Ghost Admin</p>
[Browse &rarr;](/docs/global-form-checkbox--docs)
</div>
<div>
<img src={AppsIcon} className="icon pink" />
<h4>App specific</h4>
<p>Reusable components of individual React apps in Ghost Admin</p>
[Example &rarr;](/docs/settings-setting-section--docs)
</div>
</section>
The system uses Storybook — if you're new to it, we recommend reading about what it is and how to use it, on [storybook.js.org](https://storybook.js.org).
<br/><br/>
</div>

Binary file not shown.

After

Width:  |  Height:  |  Size: 611 KiB

View File

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="-0.75 -0.75 32 32" height="32" width="32"><defs></defs><title>app-window-multiple</title><path d="M2.872083333333333 2.8631875h17.15625s1.90625 0 1.90625 1.90625v13.34375s0 1.90625 -1.90625 1.90625H2.872083333333333s-1.90625 0 -1.90625 -1.90625v-13.34375s0 -1.90625 1.90625 -1.90625" fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="1.5"></path><path d="m0.9658333333333333 8.5819375 20.96875 0" fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="1.5"></path><path d="M25.747083333333332 8.5819375v13.34375a1.90625 1.90625 0 0 1 -1.90625 1.90625H6.684583333333332" fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="1.5"></path><path d="M29.559583333333332 12.394437499999999v13.34375a1.90625 1.90625 0 0 1 -1.90625 1.90625H10.497083333333332" fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="1.5"></path></svg>

After

Width:  |  Height:  |  Size: 1.0 KiB

View File

@ -0,0 +1 @@
<svg viewBox="-0.75 -0.75 32 32" xmlns="http://www.w3.org/2000/svg" height="32" width="32"><path d="M13.34375 15.918458333333332a1.242875 1.242875 0 0 1 -1.2390625 1.2454166666666666h-9.9125a1.2416041666666666 1.2416041666666666 0 0 1 -1.2390625 -1.2454166666666666V2.2061666666666664a1.242875 1.242875 0 0 1 1.2403333333333333 -1.2454166666666666l9.9125 0.016520833333333332a1.242875 1.242875 0 0 1 1.2377916666666666 1.2454166666666666Z" fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="1.5"></path><path d="M29.541791666666665 8.316333333333333a1.2314375 1.2314375 0 0 1 -1.2390625 1.2225416666666666h-9.9125a1.2301666666666666 1.2301666666666666 0 0 1 -1.2390625 -1.2225416666666666V2.1998125a1.2301666666666666 1.2301666666666666 0 0 1 1.2365208333333333 -1.2225416666666666l9.9125 -0.016520833333333332a1.2327083333333333 1.2327083333333333 0 0 1 1.2416041666666666 1.2225416666666666Z" fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="1.5"></path><path d="M13.34375 28.32814583333333a1.2339791666666666 1.2339791666666666 0 0 1 -1.2416041666666666 1.2263541666666666l-9.9125 -0.016520833333333332a1.2314375 1.2314375 0 0 1 -1.2365208333333333 -1.2250833333333333V22.20145833333333a1.2314375 1.2314375 0 0 1 1.2390625 -1.2250833333333333h9.9125a1.2327083333333333 1.2327083333333333 0 0 1 1.2390625 1.2250833333333333Z" fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="1.5"></path><path d="M18.391499999999997 29.554499999999997a1.242875 1.242875 0 0 1 -1.2403333333333333 -1.2466875V14.5980625a1.242875 1.242875 0 0 1 1.2390625 -1.2466875h9.9125a1.242875 1.242875 0 0 1 1.2390625 1.2466875v13.693229166666667a1.2441458333333333 1.2441458333333333 0 0 1 -1.2377916666666666 1.2466875Z" fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="1.5"></path></svg>

After

Width:  |  Height:  |  Size: 1.9 KiB

View File

@ -0,0 +1 @@
<svg viewBox="-0.75 -0.75 32 32" xmlns="http://www.w3.org/2000/svg" height="32" width="32"><path d="M0.953125 15.253812499999999a14.296875 14.296875 0 1 0 28.59375 0 14.296875 14.296875 0 1 0 -28.59375 0Z" fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="1.5"></path><path d="m9.53125 10.488187499999999 11.4375 0" fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="1.5"></path><path d="m9.53125 15.253812499999999 11.4375 0" fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="1.5"></path><path d="m9.53125 20.0194375 11.4375 0" fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="1.5"></path></svg>

After

Width:  |  Height:  |  Size: 776 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 34 KiB

After

Width:  |  Height:  |  Size: 139 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 51 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.2 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 MiB

View File

@ -0,0 +1 @@
<svg viewBox="-1 -1 32 32" xmlns="http://www.w3.org/2000/svg" height="32" width="32"><path d="M24.375 0.9375a0.9375 0.9375 0 0 1 0.9375 0.9375v7.5a0.9375 0.9375 0 0 1 -0.9375 0.9375h-18.75a0.9375 0.9375 0 0 1 -0.9375 -0.9375v-7.5A0.9375 0.9375 0 0 1 5.625 0.9375h2.8125a0.9375 0.9375 0 0 1 0.9375 0.9375v0.9375a1.875 1.875 0 0 0 3.75 0V1.875a0.9375 0.9375 0 0 1 0.9375 -0.9375h1.875a0.9375 0.9375 0 0 1 0.9375 0.9375v0.9375a1.875 1.875 0 0 0 3.75 0V1.875a0.9375 0.9375 0 0 1 0.9375 -0.9375Z" fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2"></path><path d="m6.5625 10.3125 -1.875 18.75 20.625 0 -1.875 -18.75 -16.875 0z" fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2"></path><path d="M17.8125 29.0625h-5.625V22.5a2.8125 2.8125 0 0 1 5.625 0Z" fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2"></path><path d="m0.9375 29.0625 28.125 0" fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2"></path></svg>

After

Width:  |  Height:  |  Size: 1.1 KiB