Compare commits

..

38 Commits

Author SHA1 Message Date
konnorrogers
80a13cc754 prettier 2024-04-17 11:39:56 -04:00
konnorrogers
c70dc7f92f fix themer 2024-04-17 11:28:15 -04:00
konnorrogers
59660d6458 prettier 2024-03-21 11:17:31 -04:00
konnorrogers
d47bf56493 fix resolver stuff 2024-03-14 17:16:10 -04:00
konnorrogers
6acb817688 add missing assets for kitchen sink 2024-03-14 17:10:22 -04:00
konnorrogers
772be3f2ec move images to right spot 2024-03-14 16:34:53 -04:00
konnorrogers
32e6664055 add web fonts 2024-03-14 16:21:05 -04:00
konnorrogers
029624b869 fix css conflicts in kitchen sink 2024-03-14 13:04:22 -04:00
konnorrogers
c4ccfafccc Merge branch 'next' of https://github.com/shoelace-style/webawesome into konnorrogers-pac 2024-03-14 12:56:03 -04:00
konnorrogers
d34352c5a9 working on kitchen sink 2024-03-14 12:52:05 -04:00
Konnor Rogers
8e5e039af8 Rename Layout to Page (#65)
* working on layouts

* fix buildS

* first layout converted to sportawesome

* working on playgrounds

* continued work on layouts

* working on astro layout

* light-pen 3

* rename layout to page

* prettier

* add vercel.json

* add vercel.json

* add vercel.json

* add vercel.json

* add vercel.json

* add sandbox-settings

* add sandbox-settings

* add sandbox-settings

* maybe now?

* remove sandbox-settings

* remove vercel.json

* rename to pagE

* fix grid for navigation

* prettier

* fix stuff

* fix search

* prettier
2024-03-14 12:47:28 -04:00
konnorrogers
d5a6390fcd Merge branch 'next' of https://github.com/shoelace-style/webawesome into konnorrogers-pac 2024-03-13 16:34:50 -04:00
Cory LaViska
2e725a2d93 prettier 2024-03-12 16:02:31 -04:00
Cory LaViska
c139865635 fix padding 2024-03-12 15:59:22 -04:00
Cory LaViska
f59c544fbe update content 2024-03-12 15:06:28 -04:00
Cory LaViska
28bdcae2c6 fix layout to be more font awesomey 2024-03-12 15:00:58 -04:00
konnorrogers
b1530d0773 Fix dev server infinite reload 2024-03-12 14:16:11 -04:00
Konnor Rogers
5feee64425 Font Awesome theme 👀 (#64)
* font awesome site theme

* separate font awesome.css

* prettier

* remove image borders

* fix search and prism

* select first item

* 30ms'

* fix double render

* fix for turbo loading

* fix preview

* prettier

* fix pagefind

* prettier
2024-03-12 13:45:33 -04:00
Konnor Rogers
9647259b5f fix the test suite (#63)
* fix the test suite

* prettier

* fix the test suite

* prettier
2024-03-11 12:23:25 -04:00
Cory LaViska
84e276ae10 Merge pull request #32 from shoelace-style/prepare
Backport 1868
2024-03-06 07:56:34 -05:00
Cory LaViska
4718c3d815 move to prepare 2024-03-06 07:56:24 -05:00
Cory LaViska
aa1bfb0885 Merge branch 'next' into prepare 2024-03-06 07:54:52 -05:00
Cory LaViska
acf2055768 Merge pull request #31 from shoelace-style/efficient-style-imports
Backport 1861
2024-03-06 07:54:16 -05:00
Cory LaViska
ef4d2fac40 Merge branch 'next' into efficient-style-imports 2024-03-06 07:53:52 -05:00
Cory LaViska
b53c1d940a backport 1880 2024-02-21 13:33:00 -05:00
Cory LaViska
edd62490f8 backport 1839 2024-02-20 15:01:35 -05:00
Cory LaViska
96a381d3a3 backport 1879 2024-02-20 14:18:48 -05:00
Cory LaViska
a2a72de2cf backport 1788 2024-02-20 14:13:23 -05:00
Cory LaViska
9a51e69320 backport 1878 2024-02-20 14:00:49 -05:00
Cory LaViska
07be57847d backport 1874 2024-02-20 13:48:57 -05:00
Cory LaViska
0095ca5fe7 backport 1877 2024-02-20 12:53:38 -05:00
Cory LaViska
a1b1d594aa backport 1868 2024-02-12 12:50:55 -05:00
Cory LaViska
531a2f1634 backport 1861 2024-02-09 11:03:15 -05:00
Cory LaViska
cb1c423aea backport 1862 2024-02-09 10:05:14 -05:00
Cory LaViska
93306c99ce backport 1800, 1860 2024-02-09 09:58:13 -05:00
Cory LaViska
5f8c69064c backport 1852 2024-02-09 09:46:46 -05:00
Cory LaViska
f51a09ddf0 backport 1840 2024-02-08 12:46:08 -05:00
Konnor Rogers
92533c0297 Convert to Starlight (#22)
* first pass at starlight

* converting to starlight

* working on converting to starlight

* working on data

* watch custom-elements.json

* turn on pagefind

* add component meta data

* fix renderings / overrides.

* fix mdx logo

* continue starlight work

* building site

* get global styles + reloads working

* themer fixes

* adding additional headings

* working on dynamic content

* have TableOfContents.astro push to TOC

* working on code stuff

* remove code preview

* deploy

* add patch package

* patch in build

* patch in build

* remove {% raw %} calls

* convert to starlight...complete

* prettier

* update lockfile

* merge main

* fix index.mdx

* prettier'

* fix small things

* docs updates

* add dark mode shortcut

* prettier

* prettier

* prettier

* remove pagefind from public

* add twitteR

* prettier

* fix tests

* prettier
2024-02-05 11:02:14 -05:00
1187 changed files with 19190 additions and 24507 deletions

View File

@@ -35,7 +35,7 @@ This Code of Conduct applies within all project spaces, and it also applies when
## Enforcement
Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by contacting the project team at cory@abeautifulsite.net. All complaints will be reviewed and investigated and will result in a response that is deemed necessary and appropriate to the circumstances. The project team is obligated to maintain confidentiality with regard to the reporter of an incident. Further details of specific enforcement policies may be posted separately.
Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by contacting the project team at support@fontawesome.com. All complaints will be reviewed and investigated and will result in a response that is deemed necessary and appropriate to the circumstances. The project team is obligated to maintain confidentiality with regard to the reporter of an incident. Further details of specific enforcement policies may be posted separately.
Project maintainers who do not follow or enforce the Code of Conduct in good faith may face temporary or permanent repercussions as determined by other members of the project's leadership.

1
.github/FUNDING.yml vendored
View File

@@ -1 +0,0 @@
github: [claviska]

2
.github/SECURITY.md vendored
View File

@@ -2,6 +2,6 @@
We take security issues in Web Awesome very seriously and appreciate your efforts to disclose your findings responsibly.
To report a security issue, email [cory@fontawesome.com](mailto:cory@abeautifulsite.net) and include "WEB AWESOME SECURITY" in the subject line.
To report a security issue, email [support@fontawesome.com](mailto:support@fontawesome.com) and include "WEB AWESOME SECURITY" in the subject line.
We'll respond as soon as possible and keep you updated throughout the process.

2
.gitignore vendored
View File

@@ -5,6 +5,8 @@ package.json
package-lock.json
dist
docs/assets/images/sprite.svg
docs/public/pagefind
node_modules
src/react
cdn
.astro

View File

@@ -1,4 +1,5 @@
*.hbs
*.mdx
.cache
.github
cspell.json

View File

@@ -1,7 +1,5 @@
# Web Awesome
A forward-thinking library of web components.
- Works with all frameworks 🧩
- Works with CDNs 🚛
- Fully customizable with CSS 🎨

View File

@@ -21,7 +21,6 @@
"cdndir",
"chatbubble",
"checkmark",
"claviska",
"Clippy",
"codebases",
"codepen",
@@ -86,7 +85,6 @@
"Kool",
"labelledby",
"Laravel",
"LaViska",
"linkify",
"listbox",
"listitem",
@@ -173,6 +171,7 @@
"valpha",
"valuenow",
"valuetext",
"viewports",
"Vuejs",
"WCAG",
"webawesome",

4
docs/.vscode/extensions.json vendored Normal file
View File

@@ -0,0 +1,4 @@
{
"recommendations": ["astro-build.astro-vscode"],
"unwantedRecommendations": []
}

11
docs/.vscode/launch.json vendored Normal file
View File

@@ -0,0 +1,11 @@
{
"version": "0.2.0",
"configurations": [
{
"command": "./node_modules/.bin/astro dev",
"name": "Development server",
"request": "launch",
"type": "node-terminal"
}
]
}

View File

@@ -1,349 +0,0 @@
{% extends "default.njk" %}
{# Find the component based on the `tag` front matter #}
{% set component = getComponent('wa-' + page.fileSlug) %}
{% block content %}
{# Determine the badge variant #}
{% if component.status == 'stable' %}
{% set badgeVariant = 'brand' %}
{% elseif component.status == 'experimental' %}
{% set badgeVariant = 'warning' %}
{% elseif component.status == 'planned' %}
{% set badgeVariant = 'neutral' %}
{% elseif component.status == 'deprecated' %}
{% set badgeVariant = 'danger' %}
{% else %}
{% set badgeVariant = 'neutral' %}
{% endif %}
{# Header #}
<header class="component-header">
<h1>{{ component.name | classNameToComponentName }}</h1>
<div class="component-header__tag">
<code>&lt;{{ component.tagName }}&gt; | {{ component.name }}</code>
</div>
<div class="component-header__info">
<wa-badge variant="neutral" pill>
Since {{component.since or '?' }}
</wa-badge>
<wa-badge variant="{{ badgeVariant }}" pill style="text-transform: capitalize;">
{{ component.status }}
</wa-badge>
</div>
</header>
<p class="component-summary">
{% if component.summary %}
{{ component.summary | markdownInline | safe }}
{% endif %}
</p>
{# Markdown content #}
{{ content | safe }}
{# Importing #}
<h2>Importing</h2>
<p>
If you're using the autoloader or the traditional loader, you can ignore this section. Otherwise, feel free to use
any of the following snippets to <a href="/getting-started/installation#cherry-picking">cherry pick</a> this component.
</p>
<wa-tab-group>
<wa-tab slot="nav" panel="script">Script</wa-tab>
<wa-tab slot="nav" panel="import">Import</wa-tab>
<wa-tab slot="nav" panel="bundler">Bundler</wa-tab>
<wa-tab slot="nav" panel="react">React</wa-tab>
<wa-tab-panel name="script">
<p>
To import this component from <a href="https://www.jsdelivr.com/package/npm/@shoelace-style/shoelace">the CDN</a>
using a script tag:
</p>
<pre><code class="language-html">&lt;script type=&quot;module&quot; src=&quot;https://cdn.jsdelivr.net/npm/@shoelace-style/shoelace@{{ meta.version }}/{{ meta.cdndir }}/{{ component.path }}&quot;&gt;&lt;/script&gt;</code></pre>
</wa-tab-panel>
<wa-tab-panel name="import">
<p>
To import this component from <a href="https://www.jsdelivr.com/package/npm/@shoelace-style/shoelace">the CDN</a>
using a JavaScript import:
</p>
<pre><code class="language-js">import 'https://cdn.jsdelivr.net/npm/@shoelace-style/shoelace@{{ meta.version }}/{{ meta.cdndir }}/{{ component.path }}';</code></pre>
</wa-tab-panel>
<wa-tab-panel name="bundler">
<p>
To import this component using <a href="{{ rootUrl('/getting-started/installation#bundling') }}">a bundler</a>:
</p>
<pre><code class="language-js">import '@shoelace-style/shoelace/{{ meta.npmdir }}/{{ component.path }}';</code></pre>
</wa-tab-panel>
<wa-tab-panel name="react">
<p>
To import this component as a <a href="/frameworks/react">React component</a>:
</p>
<pre><code class="language-js">import {{ component.name }} from '@shoelace-style/shoelace/{{ meta.npmdir }}/react/{{ component.tagNameWithoutPrefix }}';</code></pre>
</wa-tab-panel>
</wa-tab-group>
{# Slots #}
{% if component.slots.length %}
<h2>Slots</h2>
<table>
<thead>
<tr>
<th class="table-name">Name</th>
<th class="table-description">Description</th>
</tr>
</thead>
<tbody>
{% for slot in component.slots %}
<tr>
<td class="nowrap">
{% if slot.name %}
<code>{{ slot.name }}</code>
{% else %}
(default)
{% endif %}
</td>
<td>{{ slot.description | markdownInline | safe }}</td>
</tr>
{% endfor %}
</tbody>
</table>
<p><em>Learn more about <a href="{{ rootUrl('/getting-started/usage#slots') }}">using slots</a>.</em></p>
{% endif %}
{# Properties #}
{% if component.properties.length %}
<h2>Properties</h2>
<table>
<thead>
<tr>
<th class="table-name">Name</th>
<th class="table-description">Description</th>
<th class="table-reflects">Reflects</th>
<th class="table-type">Type</th>
<th class="table-default">Default</th>
</tr>
</thead>
<tbody>
{% for prop in component.properties %}
<tr>
<td>
<code class="nowrap">{{ prop.name }}</code>
{% if prop.attribute | length > 0 %}
{% if prop.attribute != prop.name %}
<br>
<wa-tooltip content="This attribute is different from its property">
<small>
<code class="nowrap">
{{ prop.attribute }}
</code>
</small>
</wa-tooltip>
{% endif %}
{% endif %}
</td>
<td>
{{ prop.description | markdownInline | safe }}
</td>
<td style="text-align: center;">
{% if prop.reflects %}
<wa-icon label="yes" name="check" variant="solid"></wa-icon>
{% endif %}
</td>
<td>
{% if prop.type.text %}
<code>{{ prop.type.text | trimPipes | markdownInline | safe }}</code>
{% else %}
-
{% endif %}
</td>
<td>
{% if prop.default %}
<code>{{ prop.default | markdownInline | safe }}</code>
{% else %}
-
{% endif %}
</td>
</tr>
{% endfor %}
<tr>
<td class="nowrap"><code>updateComplete</code></td>
<td>
A read-only promise that resolves when the component has
<a href="/getting-started/usage?#component-rendering-and-updating">finished updating</a>.
</td>
<td></td>
<td></td>
<td></td>
</tr>
</tbody>
</table>
<p><em>Learn more about <a href="{{ rootUrl('/getting-started/usage#attributes-and-properties') }}">attributes and properties</a>.</em></p>
{% endif %}
{# Events #}
{% if component.events.length %}
<h2>Events</h2>
<table>
<thead>
<tr>
<th class="table-name" data-flavor="html">Name</th>
<th class="table-name" data-flavor="react">React Event</th>
<th class="table-description">Description</th>
<th class="table-event-detail">Event Detail</th>
</tr>
</thead>
<tbody>
{% for event in component.events %}
<tr>
<td data-flavor="html"><code class="nowrap">{{ event.name }}</code></td>
<td data-flavor="react"><code class="nowrap">{{ event.reactName }}</code></td>
<td>{{ event.description | markdownInline | safe }}</td>
<td>
{% if event.type.text %}
<code>{{ event.type.text | trimPipes }}</code>
{% else %}
-
{% endif %}
</td>
</tr>
{% endfor %}
</tbody>
</table>
<p><em>Learn more about <a href="{{ rootUrl('/getting-started/usage#events') }}">events</a>.</em></p>
{% endif %}
{# Methods #}
{% if component.methods.length %}
<h2>Methods</h2>
<table>
<thead>
<tr>
<th class="table-name">Name</th>
<th class="table-description">Description</th>
<th class="table-arguments">Arguments</th>
</tr>
</thead>
<tbody>
{% for method in component.methods %}
<tr>
<td class="nowrap"><code>{{ method.name }}()</code></td>
<td>{{ method.description | markdownInline | safe }}</td>
<td>
{% if method.parameters.length %}
<code>
{% for param in method.parameters %}
{{ param.name }}: {{ param.type.text | trimPipes }}{% if not loop.last %},{% endif %}
{% endfor %}
</code>
{% else %}
-
{% endif %}
</td>
</tr>
{% endfor %}
</tbody>
</table>
<p><em>Learn more about <a href="{{ rootUrl('/getting-started/usage#methods') }}">methods</a>.</em></p>
{% endif %}
{# Custom Properties #}
{% if component.cssProperties.length %}
<h2>Custom Properties</h2>
<table>
<thead>
<tr>
<th class="table-name">Name</th>
<th class="table-description">Description</th>
<th class="table-default">Default</th>
</tr>
</thead>
<tbody>
{% for cssProperty in component.cssProperties %}
<tr>
<td class="nowrap"><code>{{ cssProperty.name }}</code></td>
<td>{{ cssProperty.description | markdownInline | safe }}</td>
<td>{{ cssProperty.default }}</td>
</tr>
{% endfor %}
</tbody>
</table>
<p><em>Learn more about <a href="{{ rootUrl('/getting-started/usage#custom-properties') }}">customizing CSS custom properties</a>.</em></p>
{% endif %}
{# CSS Parts #}
{% if component.cssParts.length %}
<h2>Parts</h2>
<table>
<thead>
<tr>
<th class="table-name">Name</th>
<th class="table-description">Description</th>
</tr>
</thead>
<tbody>
{% for cssPart in component.cssParts %}
<tr>
<td class="nowrap"><code>{{ cssPart.name }}</code></td>
<td>{{ cssPart.description | markdownInline | safe }}</td>
</tr>
{% endfor %}
</tbody>
</table>
<p><em>Learn more about <a href="{{ rootUrl('/getting-started/customizing/#css-parts') }}">customizing CSS parts</a>.</em></p>
{% endif %}
{# Animations #}
{% if component.animations.length %}
<h2>Animations</h2>
<table>
<thead>
<tr>
<th class="table-name">Name</th>
<th class="table-description">Description</th>
</tr>
</thead>
<tbody>
{% for animation in component.animations %}
<tr>
<td class="nowrap"><code>{{ animation.name }}</code></td>
<td>{{ animation.description | markdownInline | safe }}</td>
</tr>
{% endfor %}
</tbody>
</table>
<p><em>Learn more about <a href="{{ rootUrl('/getting-started/customizing#animations') }}">customizing animations</a>.</em></p>
{% endif %}
{# Dependencies #}
{% if component.dependencies.length %}
<h2>Dependencies</h2>
<p>This component automatically imports the following dependencies.</p>
<ul>
{% for dependency in component.dependencies %}
<li><code>&lt;{{ dependency }}&gt;</code></li>
{% endfor %}
</ul>
{% endif %}
{% endblock %}

View File

@@ -1,132 +0,0 @@
<!DOCTYPE html>
<html
lang="en"
data-layout="{{ layout }}"
data-wa-version="{{ meta.version }}"
>
<head>
{# Metadata #}
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<meta name="description" content="{{ meta.description }}" />
<title>{{ meta.title }}</title>
{# Opt out of Turbo caching #}
<meta name="turbo-cache-control">
{# Stylesheets #}
<link rel="stylesheet" href="{{ assetUrl('styles/docs.css') }}" />
<link rel="stylesheet" href="{{ assetUrl('styles/code-previews.css') }}" />
<link rel="stylesheet" href="{{ assetUrl('styles/search.css') }}" />
{# Favicons #}
<link rel="icon" href="{{ assetUrl('images/favicon.svg') }}" type="image/x-icon" />
{# Twitter Cards #}
<meta name="twitter:card" content="summary" />
<meta name="twitter:creator" content="shoelace_style" />
<meta name="twitter:image" content="{{ assetUrl(meta.image, true) }}" />
{# OpenGraph #}
<meta property="og:url" content="{{ rootUrl(page.url, true) }}" />
<meta property="og:title" content="{{ meta.title }}" />
<meta property="og:description" content="{{ meta.description }}" />
<meta property="og:image" content="{{ assetUrl(meta.image, true) }}" />
{# Web Awesome #}
<link rel="stylesheet" href="/dist/themes/applied.css" />
<link rel="stylesheet" href="/dist/themes/forms.css" />
<link id="theme-stylesheet" rel="stylesheet" href="/dist/themes/default.css" />
<link id="color-stylesheet" rel="stylesheet" href="/dist/themes/color_standard.css" />
<link id="theme-stylesheet" rel="stylesheet" href="/dist/themes/demo_patterns.css" />
<link id="theme-stylesheet" rel="stylesheet" href="/dist/themes/demo_sublayout.css" />
<script type="module" src="/dist/autoloader.js"></script>
{# Set the initial theme and menu states here to prevent flashing #}
<script>
(() => {
const prefersDark = window.matchMedia('(prefers-color-scheme: dark)').matches;
const theme = localStorage.getItem('theme') || 'auto';
document.documentElement.classList.toggle('wa-theme-default-dark', theme === 'dark' || (theme === 'auto' && prefersDark));
})();
</script>
{# Web Fonts #}
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Assistant:wght@200..800&family=Inter:wght@100..900&family=Lora:wght@400..700&family=Mulish:wght@200..1000&family=Noto+Sans+Display:wght@100..900&family=Noto+Sans+Mono:wght@100..900&family=Noto+Sans:wght@100..900&family=Noto+Serif:wght@100..900&family=Open+Sans:wght@300..800&family=Playfair+Display:wght@400..900&family=Playfair:opsz,wght@5..1200,300;5..1200,400;5..1200,500;5..1200,600&family=Quicksand:wght@300..700&family=Roboto+Flex:opsz,wght@8..144,300;8..144,400;8..144,500;8..144,600&family=Roboto+Mono:wght@300..700&family=Roboto+Serif:opsz,wght@8..144,300;8..144,400;8..144,500;8..144,600&family=Roboto+Slab:wght@300..700&family=Inconsolata:wght@200..900&display=swap" rel="stylesheet">
{# Turbo + Scroll positioning #}
<script src="{{ assetUrl('scripts/turbo.js') }}" type="module"></script>
<script src="{{ assetUrl('scripts/docs.js') }}" defer></script>
<script src="{{ assetUrl('scripts/code-previews.js') }}" defer></script>
<script src="{{ assetUrl('scripts/lunr.js') }}" defer></script>
<script src="{{ assetUrl('scripts/search.js') }}" defer></script>
</head>
<body>
<a id="skip-to-main" class="wa-visually-hidden" href="#main-content" data-smooth-link="false">
Skip to main content
</a>
{# Menu toggle #}
<button id="menu-toggle" type="button" aria-label="Menu">
<svg width="148" height="148" viewBox="0 0 148 148" xmlns="http://www.w3.org/2000/svg">
<g stroke="currentColor" stroke-width="18" fill="none" fill-rule="evenodd" stroke-linecap="round">
<path d="M9.5 125.5h129M9.5 74.5h129M9.5 23.5h129"></path>
</g>
</svg>
</button>
<aside id="sidebar" data-preserve-scroll>
<header>
<a href="/">
{% include 'logo.njk' %}
</a>
<div class="sidebar-version">
{{ meta.version }}
</div>
</header>
<div class="sidebar-buttons">
<wa-button size="small" class="repo-button repo-button--github" href="https://github.com/shoelace-style/shoelace" target="_blank">
<wa-icon slot="prefix" name="github" family="brands"></wa-icon> Code
</wa-button>
<wa-button size="small" class="repo-button repo-button--star" href="https://github.com/shoelace-style/shoelace/stargazers" target="_blank">
<wa-icon slot="prefix" name="star" variant="solid"></wa-icon> Star
</wa-button>
<wa-button size="small" class="repo-button repo-button--twitter" href="https://twitter.com/shoelace_style" target="_blank">
<wa-icon slot="prefix" name="twitter" family="brands"></wa-icon> Follow
</wa-button>
</div>
<button class="search-box" type="button" title="Press / to search" aria-label="Search" data-plugin="search">
<wa-icon name="search"></wa-icon>
<span>Search</span>
</button>
<nav>
{% include 'sidebar.njk' %}
</nav>
</aside>
{# Content #}
<main>
<a id="main-content"></a>
<article id="content" class="content{% if toc %} content--with-toc{% endif %}">
{% if toc %}
<div class="content__toc">
<ul>
<li class="top"><a href="#">{{ meta.title }}</a></li>
</ul>
</div>
{% endif %}
<div class="content__body">
{% block content %}
{{ content | safe }}
{% endblock %}
</div>
</article>
</main>
</body>
</html>

View File

@@ -1,86 +0,0 @@
<ul>
<li>
<h2>Experimental</h2>
<ul>
<li><a href="/experimental/kitchen-sink">Theme Builder</a></li>
<li><a href="/experimental/style-guide">Style Guide</a></li>
<li><a href="/experimental/form-validation">Form Validation Styles</a></li>
<li style="margin-top: .5rem;"><wa-switch id="theme-toggle">Dark mode</wa-switch></li>
<script type="module">
// Temporary dark toggle
const toggle = document.getElementById('theme-toggle');
toggle.checked = document.documentElement.classList.contains('wa-theme-default-dark');
toggle.addEventListener('wa-change', () => {
document.documentElement.classList.toggle('wa-theme-default-dark');
localStorage.setItem('theme', toggle.checked ? 'dark' : 'light');
});
</script>
<li><a href="/experimental/sandbox">Sandbox</a></li>
<li><a href="/experimental/patterns">Patterns</a></li>
</ul>
</li>
<li>
<h2>Getting Started</h2>
<ul>
<li><a href="/">Home</a></li>
<li><a href="/getting-started/installation">Installation</a></li>
<li><a href="/getting-started/usage">Usage</a></li>
<li><a href="/getting-started/themes">Themes</a></li>
<li><a href="/getting-started/customizing">Customizing</a></li>
<li><a href="/getting-started/form-controls">Form Controls</a></li>
<li><a href="/getting-started/localization">Localization</a></li>
</ul>
</li>
<li>
<h2>Frameworks</h2>
<ul>
<li><a href="/frameworks/react">React</a></li>
<li><a href="/frameworks/vue">Vue</a></li>
<li><a href="/frameworks/angular">Angular</a></li>
</ul>
</li>
<li>
<h2>Resources</h2>
<ul>
<li><a href="/resources/community">Community</a></li>
<li><a href="https://github.com/shoelace-style/shoelace/discussions">Help &amp; Support</a></li>
<li><a href="/resources/accessibility">Accessibility</a></li>
<li><a href="/resources/contributing">Contributing</a></li>
<li><a href="/resources/changelog">Changelog</a></li>
</ul>
</li>
<li>
<h2>Components</h2>
<ul>
{% for component in meta.components %}
<li>
<a href="/components/{{ component.tagName | removeWaPrefix }}">
{{ component.name | classNameToComponentName }}
</a>
</li>
{% endfor %}
</ul>
</li>
<li>
<h2>Design Tokens</h2>
<ul>
<li><a href="/tokens/typography">Typography</a></li>
<li><a href="/tokens/color">Color</a></li>
<li><a href="/tokens/spacing">Spacing</a></li>
<li><a href="/tokens/borders">Borders</a></li>
<li><a href="/tokens/shadows">Shadows</a></li>
<li><a href="/tokens/transition">Transition</a></li>
<li><a href="/tokens/z-index">Z-index</a></li>
<li><a href="/tokens/more">More Tokens</a></li>
</ul>
</li>
<li>
<h2>Tutorials</h2>
<ul>
<li><a href="/tutorials/integrating-with-laravel">Integrating with Laravel</a></li>
<li><a href="/tutorials/integrating-with-nextjs">Integrating with NextJS</a></li>
<li><a href="/tutorials/integrating-with-rails">Integrating with Rails</a></li>
</ul>
</li>
</ul>

View File

@@ -1,35 +0,0 @@
function normalizePathname(pathname) {
// Remove /index.html
if (pathname.endsWith('/index.html')) {
pathname = pathname.replace(/\/index\.html/, '');
}
// Remove trailing slashes
return pathname.replace(/\/$/, '');
}
/**
* Adds a class name to links that are currently active.
*/
module.exports = function (doc, options) {
options = {
className: 'active-link', // the class to add to active links
pathname: undefined, // the current pathname to compare
within: 'body', // element containing the target links
...options
};
const within = doc.querySelector(options.within);
if (!within) {
return doc;
}
within.querySelectorAll('a').forEach(link => {
if (normalizePathname(options.pathname) === normalizePathname(link.pathname)) {
link.classList.add(options.className);
}
});
return doc;
};

View File

@@ -1,64 +0,0 @@
const { createSlug } = require('./strings.cjs');
/**
* Turns headings into clickable, deep linkable anchors. The provided doc should be a document object provided by JSDOM.
* The same document will be returned with the appropriate DOM manipulations.
*/
module.exports = function (doc, options) {
options = {
levels: ['h1', 'h2', 'h3', 'h4', 'h5', 'h6'], // the headings to convert
className: 'anchor-heading', // the class name to add
within: 'body', // the element containing the target headings
...options
};
const within = doc.querySelector(options.within);
if (!within) {
return doc;
}
within.querySelectorAll('h1, h2, h3, h4, h5, h6').forEach(heading => {
const hasAnchor = heading.querySelector('a');
const anchor = doc.createElement('a');
let id = heading.textContent ?? '';
let suffix = 0;
// Skip heading levels we don't care about
if (!options.levels?.includes(heading.tagName.toLowerCase())) {
return;
}
// Convert dots to underscores
id = id.replace(/\./g, '_');
// Turn it into a slug
id = createSlug(id);
// Make sure it starts with a letter
if (!/^[a-z]/i.test(id)) {
id = `id_${id}`;
}
// Make sure the id is unique
const originalId = id;
while (doc.getElementById(id) !== null) {
id = `${originalId}-${++suffix}`;
}
if (hasAnchor || !id) return;
heading.setAttribute('id', id);
anchor.setAttribute('href', `#${encodeURIComponent(id)}`);
anchor.setAttribute('aria-label', `Direct link to "${heading.textContent}"`);
if (options.className) {
heading.classList.add(options.className);
}
// Append the anchor
heading.append(anchor);
});
return doc;
};

View File

@@ -1,138 +0,0 @@
let count = 1;
function escapeHtml(str) {
return String(str).replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;').replace(/"/g, '&quot;');
}
/**
* Turns code fields with the :preview suffix into interactive code previews.
*/
module.exports = function (doc, options) {
options = {
within: 'body', // the element containing the code fields to convert
...options
};
const within = doc.querySelector(options.within);
if (!within) {
return doc;
}
within.querySelectorAll('[class*=":preview"]').forEach(code => {
const pre = code.closest('pre');
if (!pre) {
return;
}
const adjacentPre = pre.nextElementSibling?.tagName.toLowerCase() === 'pre' ? pre.nextElementSibling : null;
const reactCode = adjacentPre?.querySelector('code[class$="react"]');
const sourceGroupId = `code-preview-source-group-${count}`;
const isExpanded = code.getAttribute('class').includes(':expanded');
const noCodePen = code.getAttribute('class').includes(':no-codepen');
count++;
const htmlButton = `
<button type="button"
title="Show HTML code"
class="code-preview__button code-preview__button--html"
>
HTML
</button>
`;
const reactButton = `
<button type="button" title="Show React code" class="code-preview__button code-preview__button--react">
React
</button>
`;
const codePenButton = `
<button type="button" class="code-preview__button code-preview__button--codepen" title="Edit on CodePen">
<svg
width="138"
height="26"
viewBox="0 0 138 26"
fill="none"
stroke="currentColor"
stroke-width="2.3"
stroke-linecap="round"
stroke-linejoin="round"
>
<path d="M80 6h-9v14h9 M114 6h-9 v14h9 M111 13h-6 M77 13h-6 M122 20V6l11 14V6 M22 16.7L33 24l11-7.3V9.3L33 2L22 9.3V16.7z M44 16.7L33 9.3l-11 7.4 M22 9.3l11 7.3 l11-7.3 M33 2v7.3 M33 16.7V24 M88 14h6c2.2 0 4-1.8 4-4s-1.8-4-4-4h-6v14 M15 8c-1.3-1.3-3-2-5-2c-4 0-7 3-7 7s3 7 7 7 c2 0 3.7-0.8 5-2 M64 13c0 4-3 7-7 7h-5V6h5C61 6 64 9 64 13z" />
</svg>
</button>
`;
const codePreview = `
<div class="code-preview ${isExpanded ? 'code-preview--expanded' : ''}">
<div class="code-preview__preview">
${code.textContent}
<div class="code-preview__resizer">
<wa-icon name="grip-vertical" variant="solid"></wa-icon>
</div>
</div>
<div class="code-preview__source-group" id="${sourceGroupId}">
<div class="code-preview__source code-preview__source--html" ${reactCode ? 'data-flavor="html"' : ''}>
<pre><code class="language-html">${escapeHtml(code.textContent)}</code></pre>
</div>
${
reactCode
? `
<div class="code-preview__source code-preview__source--react" data-flavor="react">
<pre><code class="language-jsx">${escapeHtml(reactCode.textContent)}</code></pre>
</div>
`
: ''
}
</div>
<div class="code-preview__buttons">
<button
type="button"
class="code-preview__button code-preview__toggle"
aria-expanded="${isExpanded ? 'true' : 'false'}"
aria-controls="${sourceGroupId}"
>
Source
<svg
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="2"
stroke-linecap="round"
stroke-linejoin="round"
>
<polyline points="6 9 12 15 18 9"></polyline>
</svg>
</button>
${reactCode ? ` ${htmlButton} ${reactButton} ` : ''}
${noCodePen ? '' : codePenButton}
</div>
</div>
`;
pre.insertAdjacentHTML('afterend', codePreview);
pre.remove();
if (adjacentPre) {
adjacentPre.remove();
}
});
// Wrap code preview scripts in anonymous functions so they don't run in the global scope
doc.querySelectorAll('.code-preview__preview script').forEach(script => {
if (script.type === 'module') {
// Modules are already scoped
script.textContent = script.innerHTML;
} else {
// Wrap non-modules in an anonymous function so they don't run in the global scope
script.textContent = `(() => { ${script.innerHTML} })();`;
}
});
return doc;
};

View File

@@ -1,23 +0,0 @@
let codeBlockId = 0;
/**
* Adds copy code buttons to code fields. The provided doc should be a document object provided by JSDOM. The same
* document will be returned with the appropriate DOM manipulations.
*/
module.exports = function (doc) {
doc.querySelectorAll('pre > code').forEach(code => {
const pre = code.closest('pre');
const button = doc.createElement('wa-copy-button');
if (!code.id) {
code.id = `code-block-${++codeBlockId}`;
}
button.classList.add('copy-code-button');
button.setAttribute('from', code.id);
pre.append(button);
});
return doc;
};

View File

@@ -1,41 +0,0 @@
const { isExternalLink } = require('./strings.cjs');
/**
* Transforms external links to make them safer and optionally add a target. The provided doc should be a document
* object provided by JSDOM. The same document will be returned with the appropriate DOM manipulations.
*/
module.exports = function (doc, options) {
options = {
className: 'external-link', // the class name to add to links
noopener: true, // sets rel="noopener"
noreferrer: true, // sets rel="noreferrer"
ignore: () => false, // callback function to filter links that should be ignored
within: 'body', // element that contains the target links
target: '', // sets the target attribute
...options
};
const within = doc.querySelector(options.within);
if (within) {
within.querySelectorAll('a').forEach(link => {
if (isExternalLink(link) && !options.ignore(link)) {
link.classList.add(options.className);
const rel = [];
if (options.noopener) rel.push('noopener');
if (options.noreferrer) rel.push('noreferrer');
if (rel.length) {
link.setAttribute('rel', rel.join(' '));
}
if (options.target) {
link.setAttribute('target', options.target);
}
}
});
}
return doc;
};

View File

@@ -1,63 +0,0 @@
const Prism = require('prismjs');
const PrismLoader = require('prismjs/components/index.js');
PrismLoader('diff');
PrismLoader.silent = true;
/** Highlights a code string. */
function highlight(code, language) {
const alias = language.replace(/^diff-/, '');
const isDiff = /^diff-/i.test(language);
// Auto-load the target language
if (!Prism.languages[alias]) {
PrismLoader(alias);
if (!Prism.languages[alias]) {
throw new Error(`Unsupported language for code highlighting: "${language}"`);
}
}
// Register diff-* languages to use the diff grammar
if (isDiff) {
Prism.languages[language] = Prism.languages.diff;
}
return Prism.highlight(code, Prism.languages[language], language);
}
/**
* Highlights all code fields that have a language parameter. If the language has a colon in its name, the first chunk
* will be the language used and additional chunks will be applied as classes to the `<pre>`. For example, a code field
* tagged with "html:preview" will be rendered as `<pre class="language-html preview">`.
*
* The provided doc should be a document object provided by JSDOM. The same document will be returned with the
* appropriate DOM manipulations.
*/
module.exports = function (doc) {
doc.querySelectorAll('pre > code[class]').forEach(code => {
// Look for class="language-*" and split colons into separate classes
code.classList.forEach(className => {
if (className.startsWith('language-')) {
//
// We use certain suffixes to indicate code previews, expanded states, etc. The class might look something like
// this:
//
// class="language-html:preview:expanded"
//
// The language will always come first, so we need to drop the "language-" prefix and everything after the first
// color to get the highlighter language.
//
const language = className.replace(/^language-/, '').split(':')[0];
try {
code.innerHTML = highlight(code.textContent ?? '', language);
} catch (err) {
// Language not found, skip it
}
}
});
});
return doc;
};

View File

@@ -1,75 +0,0 @@
const MarkdownIt = require('markdown-it');
const markdownItContainer = require('markdown-it-container');
const markdownItIns = require('markdown-it-ins');
const markdownItKbd = require('markdown-it-kbd');
const markdownItMark = require('markdown-it-mark');
const markdownItReplaceIt = require('markdown-it-replace-it');
const markdown = MarkdownIt({
html: true,
xhtmlOut: false,
breaks: false,
langPrefix: 'language-',
linkify: false,
typographer: false
});
// Third-party plugins
markdown.use(markdownItContainer);
markdown.use(markdownItIns);
markdown.use(markdownItKbd);
markdown.use(markdownItMark);
markdown.use(markdownItReplaceIt);
// Callouts
['tip', 'warning', 'danger'].forEach(type => {
const variant = type === 'tip' ? 'brand' : type;
let icon = 'circle-info';
if (type === 'warning') icon = 'triangle-exclamation';
if (type === 'danger') icon = 'circle-exclamation';
markdown.use(markdownItContainer, type, {
render: function (tokens, idx) {
if (tokens[idx].nesting === 1) {
return `
<wa-alert class="callout" variant="${variant}" open>
<wa-icon slot="icon" name="${icon}" variant="regular"></wa-icon>
`;
}
return '</wa-alert>\n';
}
});
});
// Asides
markdown.use(markdownItContainer, 'aside', {
render: function (tokens, idx) {
if (tokens[idx].nesting === 1) {
return `<aside>`;
}
return '</aside>\n';
}
});
// Details
markdown.use(markdownItContainer, 'details', {
validate: params => params.trim().match(/^details\s+(.*)$/),
render: (tokens, idx) => {
const m = tokens[idx].info.trim().match(/^details\s+(.*)$/);
if (tokens[idx].nesting === 1) {
return `<details>\n<summary><span>${markdown.utils.escapeHtml(m[1])}</span></summary>\n`;
}
return '</details>\n';
}
});
// Replace [#1234] with a link to GitHub issues
markdownItReplaceIt.replacements.push({
name: 'github-issues',
re: /\[#([0-9]+)\]/gs,
sub: '<a href="https://github.com/shoelace-style/shoelace/issues/$1">#$1</a>',
html: true,
default: true
});
module.exports = markdown;

View File

@@ -1,26 +0,0 @@
const { format } = require('prettier');
/** Formats markup using prettier. */
module.exports = function (content, options) {
options = {
arrowParens: 'avoid',
bracketSpacing: true,
htmlWhitespaceSensitivity: 'css',
insertPragma: false,
bracketSameLine: false,
jsxSingleQuote: false,
parser: 'html',
printWidth: 120,
proseWrap: 'preserve',
quoteProps: 'as-needed',
requirePragma: false,
semi: true,
singleQuote: true,
tabWidth: 2,
trailingComma: 'none',
useTabs: false,
...options
};
return format(content, options);
};

View File

@@ -1,24 +0,0 @@
/**
* @typedef {object} Replacement
* @property {string | RegExp} pattern
* @property {string} replacement
*/
/**
* @typedef {Array<Replacement>} Replacements
*/
/**
* @param {Document} content
* @param {Replacements} replacements
*/
module.exports = function (content, replacements) {
/** This seems trivial, but by assigning to a string first, THEN using innerHTML after iterating over every replacement, we reduce the calculations of JSDOM. At the time of writing benchmarks show a reduction from 9seconds to 3 seconds by doing so. */
let html = content.body.innerHTML;
replacements.forEach(replacement => {
html = html.replaceAll(replacement.pattern, replacement.replacement);
});
content.body.innerHTML = html;
};

View File

@@ -1,21 +0,0 @@
/**
* Turns headings into clickable, deep linkable anchors. The provided doc should be a document object provided by JSDOM.
* The same document will be returned with the appropriate DOM manipulations.
*/
module.exports = function (doc, options) {
const tables = [...doc.querySelectorAll('table')];
options = {
className: 'table-scroll', // the class name to add to the table's container
...options
};
tables.forEach(table => {
const div = doc.createElement('div');
div.classList.add(options.className);
table.insertAdjacentElement('beforebegin', div);
div.append(table);
});
return doc;
};

View File

@@ -1,16 +0,0 @@
const slugify = require('slugify');
/** Creates a slug from an arbitrary string of text. */
module.exports.createSlug = function (text) {
return slugify(String(text), {
remove: /[^\w|\s]/g,
lower: true
});
};
/** Determines whether or not a link is external. */
module.exports.isExternalLink = function (link) {
// We use the "internal" hostname when initializing JSDOM so we know that those are local links
if (!link.hostname || link.hostname === 'internal') return false;
return true;
};

View File

@@ -1,42 +0,0 @@
/**
* Generates an in-page table of contents based on headings.
*/
module.exports = function (doc, options) {
options = {
levels: ['h2'], // headings to include (they must have an id)
container: 'nav', // the container to append links to
listItem: true, // if true, links will be wrapped in <li>
within: 'body', // the element containing the headings to summarize
...options
};
const container = doc.querySelector(options.container);
const within = doc.querySelector(options.within);
const headingSelector = options.levels.map(h => `${h}[id]`).join(', ');
if (!container || !within) {
return doc;
}
within.querySelectorAll(headingSelector).forEach(heading => {
const listItem = doc.createElement('li');
const link = doc.createElement('a');
const level = heading.tagName.slice(1);
link.href = `#${heading.id}`;
link.textContent = heading.textContent;
if (options.listItem) {
// List item + link
listItem.setAttribute('data-level', level);
listItem.append(link);
container.append(listItem);
} else {
// Link only
link.setAttribute('data-level', level);
container.append(link);
}
});
return doc;
};

View File

@@ -1,23 +0,0 @@
const smartquotes = require('smartquotes');
smartquotes.replacements.push([/---/g, '\u2014']); // em dash
smartquotes.replacements.push([/--/g, '\u2013']); // en dash
smartquotes.replacements.push([/\.\.\./g, '\u2026']); // ellipsis
smartquotes.replacements.push([/\(c\)/gi, '\u00A9']); // copyright
smartquotes.replacements.push([/\(r\)/gi, '\u00AE']); // registered trademark
smartquotes.replacements.push([/\?!/g, '\u2048']); // ?!
smartquotes.replacements.push([/!!/g, '\u203C']); // !!
smartquotes.replacements.push([/\?\?/g, '\u2047']); // ??
smartquotes.replacements.push([/([0-9]\s?)-(\s?[0-9])/g, '$1\u2013$2']); // number ranges use en dash
/**
* Improves typography by adding smart quotes and similar corrections within the specified element(s).
*
* The provided doc should be a document object provided by JSDOM. The same document will be returned with the
* appropriate DOM manipulations.
*/
module.exports = function (doc, selector = 'body') {
const elements = [...doc.querySelectorAll(selector)];
elements.forEach(el => smartquotes.element(el));
return doc;
};

View File

@@ -0,0 +1,3 @@
<svg width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M11.63 3.625C11.63 4.27911 11.2435 4.84296 10.6865 5.10064L14 8L17.2622 7.34755C17.0968 7.10642 17 6.81452 17 6.5C17 5.67157 17.6716 5 18.5 5C19.3284 5 20 5.67157 20 6.5C20 7.31157 19.3555 7.9726 18.5504 7.99917L15.0307 15.8207C14.7077 16.5384 13.9939 17 13.2068 17H6.79317C6.00615 17 5.29229 16.5384 4.96933 15.8207L1.44963 7.99917C0.64452 7.9726 0 7.31157 0 6.5C0 5.67157 0.671573 5 1.5 5C2.32843 5 3 5.67157 3 6.5C3 6.81452 2.9032 7.10642 2.73777 7.34755L6 8L9.31702 5.09761C8.76346 4.83855 8.38 4.27656 8.38 3.625C8.38 2.72754 9.10754 2 10.005 2C10.9025 2 11.63 2.72754 11.63 3.625Z" fill="currentColor"/>
</svg>

After

Width:  |  Height:  |  Size: 722 B

View File

@@ -1,206 +0,0 @@
//
// Sidebar
//
// When the sidebar is hidden, we apply the inert attribute to prevent focus from reaching it. Due to the many states
// the sidebar can have (e.g. static, hidden, expanded), we test for visibility by checking to see if it's placed
// offscreen or not. Then, on resize/transition we make sure to update the attribute accordingly.
//
(() => {
function getSidebar() {
return document.getElementById('sidebar');
}
function isSidebarOpen() {
return document.documentElement.classList.contains('sidebar-open');
}
function isSidebarVisible() {
return getSidebar().getBoundingClientRect().x >= 0;
}
function toggleSidebar(force) {
const isOpen = typeof force === 'boolean' ? force : !isSidebarOpen();
return document.documentElement.classList.toggle('sidebar-open', isOpen);
}
function updateInert() {
getSidebar().inert = !isSidebarVisible();
}
// Toggle the menu
document.addEventListener('click', event => {
const menuToggle = event.target.closest('#menu-toggle');
if (!menuToggle) return;
toggleSidebar();
});
// Update the sidebar's inert state when the window resizes and when the sidebar transitions
window.addEventListener('resize', () => toggleSidebar(false));
document.addEventListener('transitionend', event => {
const sidebar = event.target.closest('#sidebar');
if (!sidebar) return;
updateInert();
});
// Close when a menu item is selected on mobile
document.addEventListener('click', event => {
const sidebar = event.target.closest('#sidebar');
const link = event.target.closest('a');
if (!sidebar || !link) return;
if (isSidebarOpen()) {
toggleSidebar();
}
});
// Close when open and escape is pressed
document.addEventListener('keydown', event => {
if (event.key === 'Escape' && isSidebarOpen()) {
event.stopImmediatePropagation();
toggleSidebar();
}
});
// Close when clicking outside of the sidebar
document.addEventListener('mousedown', event => {
if (isSidebarOpen() & !event.target?.closest('#sidebar, #menu-toggle')) {
event.stopImmediatePropagation();
toggleSidebar();
}
});
updateInert();
})();
//
// Open details when printing
//
(() => {
const detailsOpenOnPrint = new Set();
window.addEventListener('beforeprint', () => {
detailsOpenOnPrint.clear();
document.querySelectorAll('details').forEach(details => {
if (details.open) {
detailsOpenOnPrint.add(details);
}
details.open = true;
});
});
window.addEventListener('afterprint', () => {
document.querySelectorAll('details').forEach(details => {
details.open = detailsOpenOnPrint.has(details);
});
detailsOpenOnPrint.clear();
});
})();
//
// Smooth links
//
(() => {
document.addEventListener('click', event => {
const link = event.target.closest('a');
const id = (link?.hash ?? '').substr(1);
const isFragment = link?.hasAttribute('href') && link?.getAttribute('href').startsWith('#');
if (!link || !isFragment || link.getAttribute('data-smooth-link') === 'false') {
return;
}
// Scroll to the top
if (link.hash === '') {
event.preventDefault();
window.scroll({ top: 0, behavior: 'smooth' });
history.pushState(undefined, undefined, location.pathname);
}
// Scroll to an id
if (id) {
const target = document.getElementById(id);
if (target) {
event.preventDefault();
window.scroll({ top: target.offsetTop, behavior: 'smooth' });
history.pushState(undefined, undefined, `#${id}`);
}
}
});
})();
//
// Table of Contents scrollspy
//
(() => {
// This will be stale if its not a function.
const getLinks = () => [...document.querySelectorAll('.content__toc a')];
const linkTargets = new WeakMap();
const visibleTargets = new WeakSet();
const observer = new IntersectionObserver(handleIntersect, { rootMargin: '0px 0px' });
let debounce;
function handleIntersect(entries) {
entries.forEach(entry => {
// Remember which targets are visible
if (entry.isIntersecting) {
visibleTargets.add(entry.target);
} else {
visibleTargets.delete(entry.target);
}
});
updateActiveLinks();
}
function updateActiveLinks() {
const links = getLinks();
// Find the first visible target and activate the respective link
links.find(link => {
const target = linkTargets.get(link);
if (target && visibleTargets.has(target)) {
links.forEach(el => el.classList.toggle('active', el === link));
return true;
}
return false;
});
}
// Observe link targets
function observeLinks() {
getLinks().forEach(link => {
const hash = link.hash.slice(1);
const target = hash ? document.querySelector(`.content__body #${hash}`) : null;
if (target) {
linkTargets.set(link, target);
observer.observe(target);
}
});
}
observeLinks();
document.addEventListener('turbo:load', updateActiveLinks);
document.addEventListener('turbo:load', observeLinks);
})();
//
// Show custom versions in the sidebar
//
(() => {
function updateVersion() {
const el = document.querySelector('.sidebar-version');
if (!el) return;
if (location.hostname === 'next.shoelace.style') el.textContent = 'Next';
if (location.hostname === 'localhost') el.textContent = 'Development';
}
updateVersion();
document.addEventListener('turbo:load', updateVersion);
})();

View File

@@ -1,384 +0,0 @@
(() => {
// Append the search dialog to the body
const siteSearch = document.createElement('div');
const scrollbarWidth = Math.abs(window.innerWidth - document.documentElement.clientWidth);
siteSearch.classList.add('search');
siteSearch.innerHTML = `
<div class="search__overlay"></div>
<dialog id="search-dialog" class="search__dialog">
<div class="search__content">
<div class="search__header">
<div id="search-combobox" class="search__input-wrapper">
<wa-icon name="search"></wa-icon>
<input
id="search-input"
class="search__input"
type="search"
placeholder="Search"
autocomplete="off"
autocorrect="off"
autocapitalize="off"
enterkeyhint="go"
spellcheck="false"
maxlength="100"
role="combobox"
aria-autocomplete="list"
aria-expanded="true"
aria-controls="search-listbox"
aria-haspopup="listbox"
aria-activedescendant
>
<button type="button" class="search__clear-button" aria-label="Clear entry" tabindex="-1" hidden>
<wa-icon name="circle-xmark" variant="regular"></wa-icon>
</button>
</div>
</div>
<div class="search__body">
<ul
id="search-listbox"
class="search__results"
role="listbox"
aria-label="Search results"
></ul>
<div class="search__empty">No matching pages</div>
</div>
<footer class="search__footer">
<small><kbd><wa-icon label="Up" name="arrow-up"></wa-icon></kbd> <kbd><wa-icon label="Down" name="arrow-down"></wa-icon></kbd> Navigate</small>
<small><kbd><wa-icon label="Enter" name="arrow-turn-down-left"></wa-icon></kbd> Select</small>
<small><kbd>Esc</kbd> Close</small>
</footer>
</div>
</dialog>
`;
const overlay = siteSearch.querySelector('.search__overlay');
const dialog = siteSearch.querySelector('.search__dialog');
const input = siteSearch.querySelector('.search__input');
const clearButton = siteSearch.querySelector('.search__clear-button');
const results = siteSearch.querySelector('.search__results');
const version = document.documentElement.getAttribute('data-wa-version');
const key = `search_${version}`;
const searchDebounce = 50;
const animationDuration = 150;
let isShowing = false;
let searchTimeout;
let searchIndex;
let map;
const loadSearchIndex = new Promise(resolve => {
const cache = localStorage.getItem(key);
const wait = 'requestIdleCallback' in window ? requestIdleCallback : requestAnimationFrame;
// Cleanup older search indices (everything before this version)
try {
const items = { ...localStorage };
Object.keys(items).forEach(k => {
if (key > k) {
localStorage.removeItem(k);
}
});
} catch {
/* do nothing */
}
// Look for a cached index
try {
if (cache) {
const data = JSON.parse(cache);
searchIndex = window.lunr.Index.load(data.searchIndex);
map = data.map;
return resolve();
}
} catch {
/* do nothing */
}
// Wait until idle to fetch the index
wait(() => {
fetch('/assets/search.json')
.then(res => res.json())
.then(data => {
if (!window.lunr) {
console.error('The Lunr search client has not yet been loaded.');
}
searchIndex = window.lunr.Index.load(data.searchIndex);
map = data.map;
// Cache the search index for this version
if (version) {
try {
localStorage.setItem(key, JSON.stringify(data));
} catch (err) {
console.warn(`Unable to cache the search index: ${err}`);
}
}
resolve();
});
});
});
async function show() {
isShowing = true;
document.body.append(siteSearch);
document.body.classList.add('search-visible');
document.body.style.setProperty('--docs-search-scroll-lock-size', `${scrollbarWidth}px`);
clearButton.hidden = true;
requestAnimationFrame(() => input.focus());
updateResults();
dialog.showModal();
await Promise.all([
dialog.animate(
[
{ opacity: 0, transform: 'scale(.9)', transformOrigin: 'top' },
{ opacity: 1, transform: 'scale(1)', transformOrigin: 'top' }
],
{ duration: animationDuration }
).finished,
overlay.animate([{ opacity: 0 }, { opacity: 1 }], { duration: animationDuration }).finished
]);
dialog.addEventListener('mousedown', handleMouseDown);
dialog.addEventListener('keydown', handleKeyDown);
}
async function hide() {
isShowing = false;
await Promise.all([
dialog.animate(
[
{ opacity: 1, transform: 'scale(1)', transformOrigin: 'top' },
{ opacity: 0, transform: 'scale(.9)', transformOrigin: 'top' }
],
{ duration: animationDuration }
).finished,
overlay.animate([{ opacity: 1 }, { opacity: 0 }], { duration: animationDuration }).finished
]);
dialog.close();
input.blur(); // otherwise Safari will scroll to the bottom of the page on close
input.value = '';
document.body.classList.remove('search-visible');
document.body.style.removeProperty('--docs-search-scroll-lock-size');
siteSearch.remove();
updateResults();
dialog.removeEventListener('mousedown', handleMouseDown);
dialog.removeEventListener('keydown', handleKeyDown);
}
function handleInput() {
clearButton.hidden = input.value === '';
// Debounce search queries
clearTimeout(searchTimeout);
searchTimeout = setTimeout(() => updateResults(input.value), searchDebounce);
}
function handleClear() {
clearButton.hidden = true;
input.value = '';
input.focus();
updateResults();
}
function handleMouseDown(event) {
if (!event.target.closest('.search__content')) {
hide();
}
}
function handleKeyDown(event) {
// Close when pressing escape
if (event.key === 'Escape') {
event.preventDefault(); // prevent <dialog> from closing immediately so it can animate
event.stopImmediatePropagation();
hide();
return;
}
// Handle keyboard selections
if (['ArrowDown', 'ArrowUp', 'Home', 'End', 'Enter'].includes(event.key)) {
event.preventDefault();
const currentEl = results.querySelector('[data-selected="true"]');
const items = [...results.querySelectorAll('li')];
const index = items.indexOf(currentEl);
let nextEl;
if (items.length === 0) {
return;
}
switch (event.key) {
case 'ArrowUp':
nextEl = items[Math.max(0, index - 1)];
break;
case 'ArrowDown':
nextEl = items[Math.min(items.length - 1, index + 1)];
break;
case 'Home':
nextEl = items[0];
break;
case 'End':
nextEl = items[items.length - 1];
break;
case 'Enter':
currentEl?.querySelector('a')?.click();
break;
}
// Update the selected item
items.forEach(item => {
if (item === nextEl) {
input.setAttribute('aria-activedescendant', item.id);
item.setAttribute('data-selected', 'true');
nextEl.scrollIntoView({ block: 'nearest' });
} else {
item.setAttribute('data-selected', 'false');
}
});
}
}
async function updateResults(query = '') {
try {
await loadSearchIndex;
const hasQuery = query.length > 0;
const searchTerms = query
.split(' ')
.map((term, index, arr) => {
// Search API: https://lunrjs.com/guides/searching.html
if (index === arr.length - 1) {
// The last term is not mandatory and 1x fuzzy. We also duplicate it with a wildcard to match partial words
// as the user types.
return `${term}~1 ${term}*`;
} else {
// All other terms are mandatory and 1x fuzzy
return `+${term}~1`;
}
})
.join(' ');
const matches = hasQuery ? searchIndex.search(searchTerms) : [];
const hasResults = hasQuery && matches.length > 0;
siteSearch.classList.toggle('search--has-results', hasQuery && hasResults);
siteSearch.classList.toggle('search--no-results', hasQuery && !hasResults);
input.setAttribute('aria-activedescendant', '');
results.innerHTML = '';
matches.forEach((match, index) => {
const page = map[match.ref];
const li = document.createElement('li');
const a = document.createElement('a');
const displayTitle = page.title ?? '';
const displayDescription = page.description ?? '';
const displayUrl = page.url.replace(/^\//, '').replace(/\/$/, '');
let icon = 'file-text';
a.setAttribute('role', 'option');
a.setAttribute('id', `search-result-item-${match.ref}`);
if (page.url.includes('getting-started/')) {
icon = 'lightbulb';
}
if (page.url.includes('resources/')) {
icon = 'book';
}
if (page.url.includes('components/')) {
icon = 'puzzle-piece';
}
if (page.url.includes('tokens/')) {
icon = 'swatchbook';
}
if (page.url.includes('utilities/')) {
icon = 'wrench';
}
if (page.url.includes('tutorials/')) {
icon = 'gamepad';
}
li.classList.add('search__result');
li.setAttribute('role', 'option');
li.setAttribute('id', `search-result-item-${match.ref}`);
li.setAttribute('data-selected', index === 0 ? 'true' : 'false');
a.href = page.url;
a.innerHTML = `
<div class="search__result-icon" aria-hidden="true">
<wa-icon name="${icon}"></wa-icon>
</div>
<div class="search__result__details">
<div class="search__result-title"></div>
<div class="search__result-description"></div>
<div class="search__result-url"></div>
</div>
`;
a.querySelector('.search__result-title').textContent = displayTitle;
a.querySelector('.search__result-description').textContent = displayDescription;
a.querySelector('.search__result-url').textContent = displayUrl;
li.appendChild(a);
results.appendChild(li);
});
} catch {
// Ignore query errors as the user types
}
}
// Show the search dialog when clicking on data-plugin="search"
document.addEventListener('click', event => {
const searchButton = event.target.closest('[data-plugin="search"]');
if (searchButton) {
show();
}
});
// Show the search dialog when slash (or CMD+K) is pressed and focus is not inside a form element
document.addEventListener('keydown', event => {
if (
!isShowing &&
(event.key === '/' || (event.key === 'k' && (event.metaKey || event.ctrlKey))) &&
!event.composedPath().some(el => ['input', 'textarea'].includes(el?.tagName?.toLowerCase()))
) {
event.preventDefault();
show();
}
});
// Purge cache when we press CMD+CTRL+R
document.addEventListener('keydown', event => {
if ((event.metaKey || event.ctrlKey) && event.shiftKey && event.key === 'r') {
localStorage.clear();
}
});
input.addEventListener('input', handleInput);
clearButton.addEventListener('click', handleClear);
// Close when a result is selected
results.addEventListener('click', event => {
if (event.target.closest('a')) {
hide();
}
});
// We're using Turbo, so when a user searches for something, visits a result, and presses the back button, the search
// UI will still be visible but not interactive. This removes the search UI when Turbo renders a page so they don't
// get trapped.
window.addEventListener('turbo:render', () => {
document.body.classList.remove('search-visible');
document.querySelectorAll('.search__overlay, .search__dialog').forEach(el => el.remove());
});
})();

View File

@@ -1,29 +0,0 @@
import * as Turbo from 'https://cdn.jsdelivr.net/npm/@hotwired/turbo@7.3.0/+esm';
(() => {
if (!window.scrollPositions) {
window.scrollPositions = {};
}
function preserveScroll() {
document.querySelectorAll('[data-preserve-scroll').forEach(element => {
scrollPositions[element.id] = element.scrollTop;
});
}
function restoreScroll(event) {
document.querySelectorAll('[data-preserve-scroll').forEach(element => {
element.scrollTop = scrollPositions[element.id];
});
if (event.detail && event.detail.newBody) {
event.detail.newBody.querySelectorAll('[data-preserve-scroll').forEach(element => {
element.scrollTop = scrollPositions[element.id];
});
}
}
window.addEventListener('turbo:before-cache', preserveScroll);
window.addEventListener('turbo:before-render', restoreScroll);
window.addEventListener('turbo:render', restoreScroll);
})();

147
docs/astro.config.mjs Normal file
View File

@@ -0,0 +1,147 @@
import { defineConfig } from 'astro/config';
import starlight from '@astrojs/starlight';
import * as url from 'node:url';
import * as path from 'node:path';
// const __filename = url.fileURLToPath(import.meta.url);
const __dirname = url.fileURLToPath(new URL('.', import.meta.url));
import FullReload from 'vite-plugin-full-reload';
import { customElementsManifest } from './src/js/cem.js';
import { RemarkPluginFindAndReplace } from 'remark-plugin-find-and-replace';
import rehypeExternalLinks from 'rehype-external-links';
import remarkCodeHighlighter from './src/plugins/prism';
import GithubAutolink from './src/plugins/github-autolink.ts';
const version = customElementsManifest().package.version;
const cdndir = 'cdn';
const npmdir = 'dist';
function remarkFrontmatterPlugin() {
// All remark and rehype plugins return a separate function
return function (tree, file) {
const frontmatter = file.data.astro.frontmatter;
frontmatter.npmdir = npmdir;
frontmatter.cdndir = cdndir;
frontmatter.version = version;
};
}
// https://astro.build/config
export default defineConfig({
server: {
open: true,
port: 4000,
host: true
},
vite: {
server: {
watch: {
ignored: ['./public/pagefind/**/*.*'] // HERE
}
},
plugins: [
FullReload([
path.relative(__dirname, '../dist/custom-elements.json')
// path.relative(__dirname, './public/**/*.*')
])
]
},
outDir: '../_site',
site: 'https://shoelace.style',
markdown: {
syntaxHighlight: 'prism',
remarkPlugins: [
remarkFrontmatterPlugin,
RemarkPluginFindAndReplace({
replacements: [
{ pattern: '%VERSION%', replacement: version },
{ pattern: '%CDNDIR%', replacement: cdndir },
{ pattern: '%NPMDIR%', replacement: npmdir }
]
}),
GithubAutolink,
remarkCodeHighlighter
],
rehypePlugins: [
() =>
rehypeExternalLinks({
rel: ['nofollow', 'noopener', 'noreferrer'],
target: ['_blank'],
properties: {
class: 'external-link'
}
})
]
},
integrations: [
starlight({
expressiveCode: false,
title: 'Web Awesome',
social: {
github: 'https://github.com/shoelace-style/shoelace',
twitter: 'https://twitter.com/shoelace_style'
},
sidebar: [
{
label: 'Experimental',
autogenerate: { directory: 'experimental' }
},
{
label: 'Getting Started',
autogenerate: { directory: 'getting-started' }
},
{
label: 'Frameworks',
autogenerate: { directory: 'frameworks' }
},
{
label: 'Resources',
autogenerate: { directory: 'resources' },
items: [
{
label: 'Community',
link: '/resources/community'
},
{
label: 'Help & Support',
link: 'https://github.com/shoelace-style/shoelace/discussions'
},
{
label: 'Accessibility',
link: '/resources/accessibility'
},
{
label: 'Contributing',
link: '/resources/contributing'
},
{
label: 'Changelog',
link: '/resources/changelog'
}
]
},
{
label: 'Components',
autogenerate: { directory: 'components' }
},
{
label: 'Design Tokens',
autogenerate: { directory: 'tokens' }
},
{
label: 'Tutorials',
autogenerate: { directory: 'tutorials' }
}
],
// Component overrides
components: {
// Override the default `Head` component.
Head: './src/components/overrides/Head.astro',
TableOfContents: './src/components/overrides/TableOfContents.astro',
Search: './src/components/overrides/Search.astro'
}
})
]
});

View File

@@ -1,246 +0,0 @@
/* eslint-disable no-invalid-this */
const fs = require('fs');
const path = require('path');
const lunr = require('lunr');
const { capitalCase } = require('change-case');
const { JSDOM } = require('jsdom');
const { customElementsManifest, getAllComponents } = require('./_utilities/cem.cjs');
const webAwesomeFlavoredMarkdown = require('./_utilities/markdown.cjs');
const activeLinks = require('./_utilities/active-links.cjs');
const anchorHeadings = require('./_utilities/anchor-headings.cjs');
const codePreviews = require('./_utilities/code-previews.cjs');
const copyCodeButtons = require('./_utilities/copy-code-buttons.cjs');
const externalLinks = require('./_utilities/external-links.cjs');
const highlightCodeBlocks = require('./_utilities/highlight-code.cjs');
const tableOfContents = require('./_utilities/table-of-contents.cjs');
const prettier = require('./_utilities/prettier.cjs');
const scrollingTables = require('./_utilities/scrolling-tables.cjs');
const typography = require('./_utilities/typography.cjs');
const replacer = require('./_utilities/replacer.cjs');
const assetsDir = 'assets';
const cdndir = 'cdn';
const npmdir = 'dist';
const allComponents = getAllComponents();
let hasBuiltSearchIndex = false;
module.exports = function (eleventyConfig) {
//
// Global data
//
eleventyConfig.addGlobalData('baseUrl', 'https://shoelace.style/'); // the production URL
eleventyConfig.addGlobalData('layout', 'default'); // make 'default' the default layout
eleventyConfig.addGlobalData('toc', true); // enable the table of contents
eleventyConfig.addGlobalData('meta', {
title: 'Web Awesome',
description: 'A forward-thinking library of web components.',
image: 'images/og-image.png',
version: customElementsManifest.package.version,
components: allComponents,
cdndir,
npmdir
});
//
// Layout aliases
//
eleventyConfig.addLayoutAlias('default', 'default.njk');
//
// Copy assets
//
eleventyConfig.addPassthroughCopy(assetsDir);
eleventyConfig.setServerPassthroughCopyBehavior('passthrough'); // emulates passthrough copy during --serve
//
// Functions
//
// Generates a URL relative to the site's root
eleventyConfig.addNunjucksGlobal('rootUrl', (value = '', absolute = false) => {
value = path.join('/', value);
return absolute ? new URL(value, eleventyConfig.globalData.baseUrl).toString() : value;
});
// Generates a URL relative to the site's asset directory
eleventyConfig.addNunjucksGlobal('assetUrl', (value = '', absolute = false) => {
value = path.join(`/${assetsDir}`, value);
return absolute ? new URL(value, eleventyConfig.globalData.baseUrl).toString() : value;
});
// Fetches a specific component's metadata
eleventyConfig.addNunjucksGlobal('getComponent', tagName => {
const component = allComponents.find(c => c.tagName === tagName);
if (!component) {
throw new Error(
`Unable to find a component called "${tagName}". Make sure the file name is the same as the component's tag ` +
`name (minus the wa- prefix).`
);
}
return component;
});
//
// Custom markdown syntaxes
//
eleventyConfig.setLibrary('md', webAwesomeFlavoredMarkdown);
//
// Filters
//
eleventyConfig.addFilter('markdown', content => {
return webAwesomeFlavoredMarkdown.render(content);
});
eleventyConfig.addFilter('markdownInline', content => {
return webAwesomeFlavoredMarkdown.renderInline(content);
});
// Trims whitespace and pipes from the start and end of a string. Useful for CEM types, which can be pipe-delimited.
// With Prettier 3, this means a leading pipe will exist if the line wraps.
eleventyConfig.addFilter('trimPipes', content => {
return typeof content === 'string' ? content.replace(/^(\s|\|)/g, '').replace(/(\s|\|)$/g, '') : content;
});
eleventyConfig.addFilter('classNameToComponentName', className => {
let name = capitalCase(className.replace(/^Wa/, ''));
if (name === 'Qr Code') name = 'QR Code'; // manual override
return name;
});
eleventyConfig.addFilter('removeWaPrefix', tagName => {
return tagName.replace(/^wa-/, '');
});
//
// Transforms
//
eleventyConfig.addTransform('html-transform', function (content) {
// Parse the template and get a Document object
const doc = new JSDOM(content, {
// We must set a default URL so links are parsed with a hostname. Let's use a bogus TLD so we can easily
// identify which ones are internal and which ones are external.
url: `https://internal/`
}).window.document;
// DOM transforms
activeLinks(doc, { pathname: this.page.url });
anchorHeadings(doc, {
within: '#content .content__body',
levels: ['h2', 'h3', 'h4', 'h5']
});
tableOfContents(doc, {
levels: ['h2', 'h3'],
container: '#content .content__toc > ul',
within: '#content .content__body'
});
codePreviews(doc);
externalLinks(doc, { target: '_blank' });
highlightCodeBlocks(doc);
scrollingTables(doc);
copyCodeButtons(doc); // must be after codePreviews + highlightCodeBlocks
typography(doc, '#content');
replacer(doc, [
{ pattern: '%VERSION%', replacement: customElementsManifest.package.version },
{ pattern: '%CDNDIR%', replacement: cdndir },
{ pattern: '%NPMDIR%', replacement: npmdir }
]);
// Serialize the Document object to an HTML string and prepend the doctype
content = `<!DOCTYPE html>\n${doc.documentElement.outerHTML}`;
// String transforms
content = prettier(content);
return content;
});
//
// Build a search index
//
eleventyConfig.on('eleventy.after', ({ results }) => {
// We only want to build the search index on the first run so all pages get indexed.
if (hasBuiltSearchIndex) {
return;
}
const map = {};
const searchIndexFilename = path.join(eleventyConfig.dir.output, assetsDir, 'search.json');
const lunrInput = path.resolve('../node_modules/lunr/lunr.min.js');
const lunrOutput = path.join(eleventyConfig.dir.output, assetsDir, 'scripts/lunr.js');
const searchIndex = lunr(function () {
// The search index uses these field names extensively, so shortening them can save some serious bytes. The
// initial index file went from 468 KB => 401 KB by using single-character names!
this.ref('id'); // id
this.field('t', { boost: 50 }); // title
this.field('h', { boost: 25 }); // headings
this.field('c'); // content
results.forEach((result, index) => {
const url = path
.join('/', path.relative(eleventyConfig.dir.output, result.outputPath))
.replace(/\\/g, '/') // convert backslashes to forward slashes
.replace(/\/index.html$/, '/'); // convert trailing /index.html to /
const doc = new JSDOM(result.content, {
// We must set a default URL so links are parsed with a hostname. Let's use a bogus TLD so we can easily
// identify which ones are internal and which ones are external.
url: `https://internal/`
}).window.document;
const content = doc.querySelector('#content');
// Get title and headings
const title = (doc.querySelector('title')?.textContent || path.basename(result.outputPath)).trim();
const headings = [...content.querySelectorAll('h1, h2, h3, h4')]
.map(heading => heading.textContent)
.join(' ')
.replace(/\s+/g, ' ')
.trim();
// Remove code blocks and whitespace from content
[...content.querySelectorAll('code[class|=language]')].forEach(code => code.remove());
const textContent = content.textContent.replace(/\s+/g, ' ').trim();
// Update the index and map
this.add({ id: index, t: title, h: headings, c: textContent });
map[index] = { title, url };
});
});
// Copy the Lunr search client and write the index
fs.mkdirSync(path.dirname(lunrOutput), { recursive: true });
fs.copyFileSync(lunrInput, lunrOutput);
fs.writeFileSync(searchIndexFilename, JSON.stringify({ searchIndex, map }), 'utf-8');
hasBuiltSearchIndex = true;
});
//
// Send a signal to stdout that let's the build know we've reached this point
//
eleventyConfig.on('eleventy.after', () => {
console.log('[eleventy.after]');
});
//
// Dev server options (see https://www.11ty.dev/docs/dev-server/#options)
//
eleventyConfig.setServerOptions({
domDiff: false, // disable dom diffing so custom elements don't break on reload,
port: 4000, // if port 4000 is taken, 11ty will use the next one available
watch: ['cdn/**/*'] // additional files to watch that will trigger server updates (array of paths or globs)
});
//
// 11ty config
//
return {
dir: {
input: 'pages',
output: '../_site',
includes: '../_includes' // resolved relative to the input dir
},
markdownTemplateEngine: 'njk', // use Nunjucks instead of Liquid for markdown files
templateEngineOverride: ['njk'] // just Nunjucks and then markdown
};
};

View File

@@ -1,19 +0,0 @@
---
meta:
title: Page Not Found
description: "The page you were looking for couldn't be found."
permalink: 404.html
toc: false
---
<div style="text-align: center;">
# Page Not Found
![A UFO takes one of the little worker monsters](/assets/images/undraw-taken.svg)
The page you were looking for couldn't be found.
Press [[/]] to search, or [head back to the homepage](/).
</div>

View File

@@ -1,282 +0,0 @@
---
meta:
title: Blog Listing
description: TODO
toc: false
---
<style>
:root {
--docs-content-max-width: 72rem;
}
#menu-toggle,
#sidebar {
display: none;
}
main {
padding: var(--docs-content-vertical-spacing) var(--docs-content-padding);
margin: 0;
}
.anchor-heading a {
display: none;
}
.preview-container {
container: preview / inline-size;
}
</style>
<div class="preview-container wa:block-flow:3xl">
<div class="wa:blog_header wa:background:college_ruled">
<h1>Latest from the Blog</h1>
<p>Don't miss the hottest takes on art and design from respected industry experts.</p>
</div>
<div class="wa:arrange:aside-end" style="--wa-grid-size: 45ch;">
<div class="wa:block-flow:m">
<div class="wa:frame:landscape">
<img src="https://bit.ly/3Irq42Q" alt="">
</div>
<div class="wa:arrange:flex:justify-space_between">
<small><wa-format-date month="long" year="numeric"></wa-format-date></small>
<wa-tag size="small">Design</wa-tag>
</div>
<h1>Pantone's Color of the Year 2024</h1>
<p>PANTONE 13-1023 Peach Fuzz has our new year starting off with lots of warm and fuzzies.</p>
<div class="wa:arrange:flex:gap-s">
<wa-avatar label="User avatar" style="--size: 2rem;"></wa-avatar>
<small>Jane Doe</small>
</div>
</div>
<div class="wa:arrange" style="align-content: start;">
<div class="wa:arrange:side-by-side:gap-s">
<div class="wa:block-flow:xs">
<small><wa-format-date date="2023-12-16T09:17:00-04:00" month="long" year="numeric"></wa-format-date></small>
<h4>Lorem Ipsum Dolor Sit Amet</h4>
</div>
<div class="wa:frame">
<img src="https://images.pexels.com/photos/1181676/pexels-photo-1181676.jpeg?auto=compress&cs=tinysrgb&w=1260&h=750&dpr=2" alt="">
</div>
</div>
<div class="wa:arrange:side-by-side:gap-s">
<div class="wa:block-flow:xs">
<small><wa-format-date date="2023-11-16T09:17:00-04:00" month="long" year="numeric"></wa-format-date></small>
<h4>Consectetur Adipiscing Elit</h4>
</div>
<div class="wa:frame">
<img src="https://images.pexels.com/photos/4219654/pexels-photo-4219654.jpeg?auto=compress&cs=tinysrgb&w=1260&h=750&dpr=2" alt="">
</div>
</div>
<div class="wa:arrange:side-by-side:gap-s">
<div class="wa:block-flow:xs">
<small><wa-format-date date="2023-11-16T09:17:00-04:00" month="long" year="numeric"></wa-format-date></small>
<h4>Nunc Rhoncus Enim Ligula</h4>
</div>
<div class="wa:frame">
<img src="https://images.pexels.com/photos/14822510/pexels-photo-14822510.jpeg?auto=compress&cs=tinysrgb&w=1260&h=750&dpr=2" alt="">
</div>
</div>
<div class="wa:arrange:side-by-side:gap-s">
<div class="wa:block-flow:xs">
<small><wa-format-date date="2023-10-16T09:17:00-04:00" month="long" year="numeric"></wa-format-date></small>
<h4>Donec Quis Tincidunt Massa</h4>
</div>
<div class="wa:frame">
<img src="https://images.pexels.com/photos/7988116/pexels-photo-7988116.jpeg?auto=compress&cs=tinysrgb&w=1260&h=750&dpr=2" alt="">
</div>
</div>
</div>
</div>
<div class="wa:arrange:gap-m" style="background-color:var(--wa-color-brand-fill-subtle); padding:var(--wa-space-3xl); border-radius:var(--wa-panel-corners); box-shadow:var(--wa-shadow-level-1);">
<div class="wa:block-flow:s">
<h2 style="color:var(--wa-color-brand-text-on-fill)"><strong>Don't miss a thing.</strong></h2>
<p style="color:var(--wa-color-neutral-text-on-fill)">Subscribe to receive the latest posts in your inbox.</p>
</div>
<div class="wa:arrange:flex:justify-space_between:gap-s">
<wa-input class="wa:fill_space" type="email" placeholder="your@email.com">
<wa-icon name="envelope" variant="regular" label="email" slot="prefix"></wa-icon>
</wa-input>
<wa-button variant="brand">Subscribe</wa-button>
</div>
</div>
<div class="wa:block-flow:xl">
<div class="wa:arrange:flex:justify-space_between">
<h2>Arts & Culture</h2>
<wa-button outline variant="brand">
See more arts & culture posts
<wa-icon name="arrow-right" slot="suffix"></wa-icon>
</wa-button>
</div>
<div class="wa:arrange:size-s">
<div class="wa:block-flow:xs">
<div class="wa:frame:landscape">
<img src="https://images.pexels.com/photos/14363192/pexels-photo-14363192.jpeg?auto=compress&cs=tinysrgb&w=1260&h=750&dpr=2" alt="">
</div>
<div>
<small><wa-format-date date="2023-12-16T09:17:00-04:00" month="long" year="numeric"></wa-format-date></small>
</div>
<h4>Dolore eu Fugiat Nulla</h4>
</div>
<div class="wa:block-flow:xs">
<div class="wa:frame:landscape">
<img src="https://images.pexels.com/photos/8843689/pexels-photo-8843689.jpeg?auto=compress&cs=tinysrgb&w=1260&h=750&dpr=2" alt="">
</div>
<div>
<small><wa-format-date date="2023-12-16T09:17:00-04:00" month="long" year="numeric"></wa-format-date></small>
</div>
<h4>Sed Tincidunt Nibh Felis</h4>
</div>
<div class="wa:block-flow:xs">
<div class="wa:frame:landscape">
<img src="https://images.pexels.com/photos/1646953/pexels-photo-1646953.jpeg?auto=compress&cs=tinysrgb&w=1260&h=750&dpr=2" alt="">
</div>
<div>
<small><wa-format-date date="2023-12-16T09:17:00-04:00" month="long" year="numeric"></wa-format-date></small>
</div>
<h4>Curabitur Cursus Eleifend</h4>
</div>
<div class="wa:block-flow:xs">
<div class="wa:frame:landscape">
<img src="https://images.pexels.com/photos/3184188/pexels-photo-3184188.jpeg?auto=compress&cs=tinysrgb&w=1260&h=750&dpr=2" alt="">
</div>
<div>
<small><wa-format-date date="2023-12-16T09:17:00-04:00" month="long" year="numeric"></wa-format-date></small>
</div>
<h4>Ante Est Non Ligula</h4>
</div>
</div>
</div>
<div class="wa:block-flow:xl">
<div class="wa:arrange:flex:justify-space_between">
<h2>Design</h2>
<wa-button outline variant="brand">
See more design posts
<wa-icon name="arrow-right" slot="suffix"></wa-icon>
</wa-button>
</div>
<div class="wa:arrange:size-s">
<div class="wa:block-flow:xs">
<div class="wa:frame:landscape">
<img src="https://images.pexels.com/photos/1762851/pexels-photo-1762851.jpeg?auto=compress&cs=tinysrgb&w=1260&h=750&dpr=2" alt="">
</div>
<div>
<small><wa-format-date date="2023-12-16T09:17:00-04:00" month="long" year="numeric"></wa-format-date></small>
</div>
<h4>Maecenas ut Metus</h4>
</div>
<div class="wa:block-flow:xs">
<div class="wa:frame:landscape">
<img src="https://images.pexels.com/photos/196645/pexels-photo-196645.jpeg?auto=compress&cs=tinysrgb&w=1260&h=750&dpr=2" alt="">
</div>
<div>
<small><wa-format-date date="2023-12-16T09:17:00-04:00" month="long" year="numeric"></wa-format-date></small>
</div>
<h4>Morbi Vitae Sapien Non Velit</h4>
</div>
<div class="wa:block-flow:xs">
<div class="wa:frame:landscape">
<img src="https://images.pexels.com/photos/1194420/pexels-photo-1194420.jpeg?auto=compress&cs=tinysrgb&w=1260&h=750&dpr=2" alt="">
</div>
<div>
<small><wa-format-date date="2023-12-16T09:17:00-04:00" month="long" year="numeric"></wa-format-date></small>
</div>
<h4>Vestibulum Ante Ipsum Primis</h4>
</div>
<div class="wa:block-flow:xs">
<div class="wa:frame:landscape">
<img src="https://images.pexels.com/photos/1779487/pexels-photo-1779487.jpeg?auto=compress&cs=tinysrgb&w=1260&h=750&dpr=2" alt="">
</div>
<div>
<small><wa-format-date date="2023-12-16T09:17:00-04:00" month="long" year="numeric"></wa-format-date></small>
</div>
<h4>Viverra Eros Vitae</h4>
</div>
</div>
</div>
<div class="wa:block-flow:xl">
<div class="wa:arrange:flex:justify-space_between">
<h2>Web Development</h2>
<wa-button outline variant="brand">
See more web development posts
<wa-icon name="arrow-right" slot="suffix"></wa-icon>
</wa-button>
</div>
<div class="wa:arrange:size-s">
<div class="wa:block-flow:xs">
<div class="wa:frame:landscape">
<img src="https://images.pexels.com/photos/6321244/pexels-photo-6321244.jpeg?auto=compress&cs=tinysrgb&w=1260&h=750&dpr=2" alt="">
</div>
<div>
<small><wa-format-date date="2023-12-16T09:17:00-04:00" month="long" year="numeric"></wa-format-date></small>
</div>
<h4>Ante Est Non Ligula Imperdiet</h4>
</div>
<div class="wa:block-flow:xs">
<div class="wa:frame:landscape">
<img src="https://images.pexels.com/photos/5473956/pexels-photo-5473956.jpeg?auto=compress&cs=tinysrgb&w=1260&h=750&dpr=2" alt="">
</div>
<div>
<small><wa-format-date date="2023-12-16T09:17:00-04:00" month="long" year="numeric"></wa-format-date></small>
</div>
<h4>Viverra Eros Vitae</h4>
</div>
<div class="wa:block-flow:xs">
<div class="wa:frame:landscape">
<img src="https://images.pexels.com/photos/4709289/pexels-photo-4709289.jpeg?auto=compress&cs=tinysrgb&w=1260&h=750&dpr=2" alt="">
</div>
<div>
<small><wa-format-date date="2023-12-16T09:17:00-04:00" month="long" year="numeric"></wa-format-date></small>
</div>
<h4>Maecenas ut Metus</h4>
</div>
<div class="wa:block-flow:xs">
<div class="wa:frame:landscape">
<img src="https://images.pexels.com/photos/3184340/pexels-photo-3184340.jpeg?auto=compress&cs=tinysrgb&w=1260&h=750&dpr=2" alt="">
</div>
<div>
<small><wa-format-date date="2023-12-16T09:17:00-04:00" month="long" year="numeric"></wa-format-date></small>
</div>
<h4>Dolore eu Fugiat Nulla</h4>
</div>
</div>
</div>
<div class="wa:arrange:size-s wa:collection">
<div>
<h2>So Fetch&nbsp;<wa-icon name="arrow-trend-up" style="color: var(--wa-color-brand-spot);"></wa-icon></h2>
<p style="margin-block-end:0;">Other readers have been into these trending posts recently.</p>
</div>
<wa-card class="wa:block-flow:xs">
<div class="wa:frame:landscape" slot="image">
<img src="https://images.pexels.com/photos/1194420/pexels-photo-1194420.jpeg?auto=compress&cs=tinysrgb&w=1260&h=750&dpr=2" alt="">
</div>
<small><wa-format-date date="2023-12-16T09:17:00-04:00" month="long" year="numeric"></wa-format-date></small>
<h4>Vestibulum Ante Ipsum Primis</h4>
<wa-tag size="small">Design</wa-tag>
</wa-card>
<wa-card class="wa:block-flow:xs">
<div class="wa:frame:landscape" slot="image">
<img src="https://images.pexels.com/photos/4219654/pexels-photo-4219654.jpeg?auto=compress&cs=tinysrgb&w=1260&h=750&dpr=2" alt="">
</div>
<small><wa-format-date date="2023-12-16T09:17:00-04:00" month="long" year="numeric"></wa-format-date></small>
<h4>Consectetur Adipiscing Elit</h4>
<wa-tag size="small">Arts & Culture</wa-tag>
</wa-card>
<wa-card class="wa:block-flow:xs">
<div class="wa:frame:landscape" slot="image">
<img src="https://images.pexels.com/photos/6321244/pexels-photo-6321244.jpeg?auto=compress&cs=tinysrgb&w=1260&h=750&dpr=2" alt="">
</div>
<small><wa-format-date date="2023-12-16T09:17:00-04:00" month="long" year="numeric"></wa-format-date></small>
<h4>Ante Est Non Ligula Imperdiet</h4>
<wa-tag size="small">Web Development</wa-tag>
</wa-card>
</div>
<div>
<h2>Browse All Topics</h2>
<div class="wa:arrange:gap-m">
<wa-button outline>Accessibility <wa-icon name="arrow-right" slot="suffix"></wa-icon></wa-button>
<wa-button outline>Arts & Culture <wa-icon name="arrow-right" slot="suffix"></wa-icon></wa-button>
<wa-button outline>Design <wa-icon name="arrow-right" slot="suffix"></wa-icon></wa-button>
<wa-button outline>News <wa-icon name="arrow-right" slot="suffix"></wa-icon></wa-button>
<wa-button outline>Typography <wa-icon name="arrow-right" slot="suffix"></wa-icon></wa-button>
<wa-button outline>Web Development <wa-icon name="arrow-right" slot="suffix"></wa-icon></wa-button>
</div>
</div>
</div>

View File

@@ -1,171 +0,0 @@
---
meta:
title: Patterns List
description: TBD
toc: false
---
<style>
:root {
--docs-content-max-width: 72rem;
}
#menu-toggle,
#sidebar {
display: none;
}
main {
margin: var(--wa-space-xl);
}
h2:not(.code-preview h2) {
font-size: large;
}
</style>
# Blog Patterns
## Overflowing Hero
```html:preview
<section class="wa:blog-hero-overflowing">
<div class="wa:hero-backdrop">
<div class="wa:hero-content">
<div class="wa:post-details">
<wa-format-date month="long" year="numeric"></wa-format-date>
<wa-tag size="small">Design</wa-tag>
</div>
<h1>Pantone's Color of the Year 2024</h1>
<p>PANTONE 13-1023 Peach Fuzz has our new year starting off with lots of warm and fuzzies.</p>
<div class="wa:post-author">
<wa-avatar label="User avatar" style="--size: 2rem;"></wa-avatar>
<small>Jane Doe</small>
</div>
</div>
<div class="wa:frame:square">
<img src="https://bit.ly/3Irq42Q" alt="Vast, peach-colored desert">
</div>
</div>
</section>
```
## Post Body with Drop Cap
```html:preview
<section class="wa:blog-post">
<div class="wa:post-body">
<p>Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.</p>
<p>Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.</p>
</div>
</section>
```
## Footer with Reactions
```html:preview
<wa-divider></wa-divider>
<div class="wa:post-footer">
<wa-radio-group name="reactions" class="wa:post-reactions">
<wa-radio-button value="applaud"><wa-icon name="hands-clapping" label="Applaud"></wa-icon><small class="wa:count">74</small></wa-radio-button>
<wa-radio-button value="love"><wa-icon name="heart" label="Love"></wa-icon><small class="wa:count">161</small></wa-radio-button>
<wa-radio-button value="laugh"><wa-icon name="face-laugh-beam" label="Laugh"></wa-icon><small class="wa:count">9</small></wa-radio-button>
<wa-radio-button value="cry"><wa-icon name="face-sad-tear" label="Cry"></wa-icon><small class="wa:count">1</small></wa-radio-button>
</wa-radio-group>
<div class="wa:post-actions">
<wa-tooltip content="Save">
<wa-icon-button name="bookmark" label="Save"></wa-icon-button>
</wa-tooltip>
<wa-tooltip content="Share">
<wa-icon-button name="share-from-square" label="Share"></wa-icon-button>
</wa-tooltip>
</div>
</div>
```
## Comment Field
```html:preview
<article class="wa:comment-composer">
<wa-avatar label="User avatar"></wa-avatar>
<wa-textarea rows="1" placeholder="Add a comment"></wa-textarea>
</article>
```
## Comment
```html:preview
<article class="wa:comment">
<wa-avatar image="https://bit.ly/3V9kV7a" label="User avatar"></wa-avatar>
<div class="wa:comment-content">
<div class="wa:comment-bubble">
<div class="wa:comment-details">
<strong>Pedro Pascal</strong>
<small>1d</small>
</div>
<span>You expect me to search the galaxy for the home of this creature and deliver it to a race of enemy sorcerers?</span>
</div>
<div class="wa:comment-actions">
<div class="wa:reaction-like">
<wa-icon-button name="thumbs-up" label="Like"></wa-icon-button>
<small>(3)</small>
</div>
<wa-button variant="text" size="small">Reply</wa-button>
</div>
</div>
</article>
```
## Call to Action
```html:preview
<section class="wa:blog-subscribe-cta">
<div class="wa:cta-description">
<h2><strong>Don't miss a thing.</strong></h2>
<p>Subscribe to receive the latest posts in your inbox.</p>
</div>
<form>
<wa-input type="email" placeholder="your@email.com">
<wa-icon name="envelope" variant="regular" label="email" slot="prefix"></wa-icon>
</wa-input>
<wa-button type="submit" variant="brand">Subscribe</wa-button>
</form>
</section>
```
## Recommended Posts
```html:preview
<section class="wa:blog-recommended-posts">
<h2>You may also like</h2>
<div class="wa:post-list">
<article class="wa:post-link">
<div class="wa:frame:landscape">
<img src="http://bit.ly/49ThK7O" alt="">
</div>
<div class="wa:post-details">
<wa-format-date date="2024-02-16T09:17:00-04:00" month="long" year="numeric"></wa-format-date>
<wa-tag size="small">Arts & Culture</wa-tag>
</div>
<h4>Eget Consequat Libero</h4>
</article>
<article class="wa:post-link">
<div class="wa:frame:landscape">
<img src="https://bit.ly/3wHdFFp" alt="">
</div>
<div class="wa:post-details">
<wa-format-date date="2024-01-16T09:17:00-04:00" month="long" year="numeric"></wa-format-date>
<wa-tag size="small">Design</wa-tag>
</div>
<h4>Sed a Leo Tempus Aute Irure</h4>
</article>
<article class="wa:post-link">
<div class="wa:frame:landscape">
<img src="https://bit.ly/49LxbPx" alt="">
</div>
<div class="wa:post-details">
<wa-format-date date="2023-12-16T09:17:00-04:00" month="long" year="numeric"></wa-format-date>
<wa-tag size="small">Arts & Culture</wa-tag>
</div>
<h4>Ultrices Posuere Cubilia Curae</h4>
</article>
</div>
</section>
```

View File

@@ -1,175 +0,0 @@
---
meta:
title: Blog Post
description: TODO
toc: false
---
<style>
:root {
--docs-content-max-width: 72rem;
}
#menu-toggle,
#sidebar {
display: none;
}
main {
padding: var(--docs-content-vertical-spacing) var(--docs-content-padding);
margin: 0;
}
.anchor-heading a {
display: none;
}
.preview-container {
container: preview / inline-size;
}
</style>
<!-- cSpell:dictionaries lorem-ipsum -->
<div class="preview-container wa:block-flow:3xl">
<section class="wa:blog-hero-overflowing">
<div class="wa:hero-backdrop">
<div class="wa:hero-content">
<div class="wa:post-details">
<wa-format-date month="long" year="numeric"></wa-format-date>
<wa-tag size="small">Design</wa-tag>
</div>
<h1>Pantone's Color of the Year 2024</h1>
<p>PANTONE 13-1023 Peach Fuzz has our new year starting off with lots of warm and fuzzies.</p>
<div class="wa:post-author">
<wa-avatar label="User avatar" style="--size: 2rem;"></wa-avatar>
<small>Jane Doe</small>
</div>
</div>
<div class="wa:frame:square">
<img src="https://bit.ly/3Irq42Q" alt="Vast, peach-colored desert">
</div>
</div>
</section>
<section class="wa:blog-post">
<div class="wa:post-body">
<p>Morbi vitae sapien non velit feugiat consectetur. Nulla lacinia ante a diam gravida cursus. Quisque fermentum ex a nisi cursus porttitor. Praesent id laoreet mauris, id efficitur sapien. Quisque eget metus velit. Nulla sit amet tristique lectus, tincidunt lobortis velit. Proin vitae facilisis lectus. Nunc vel sapien vitae dui commodo suscipit iaculis eget felis.</p>
<p>Praesent in erat semper, fringilla tellus non, lacinia felis. Nam eu fringilla nisl. Maecenas id tortor tempus, accumsan nisi eget, bibendum arcu. Pellentesque nec enim non nisl varius iaculis. Phasellus interdum nec ex nec faucibus. Vestibulum et quam auctor massa pellentesque tempor. Sed tincidunt nibh felis, ut euismod ante volutpat aliquam. Etiam varius suscipit ornare.</p>
<p>Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia curae; Sed a leo tempus, pretium mi ac, pretium ipsum. Curabitur cursus eleifend enim. Pellentesque feugiat euismod tincidunt. Sed semper velit nunc, nec imperdiet eros varius ac. Aenean aliquam augue at venenatis volutpat. Proin a imperdiet leo. Nullam eget aliquet urna.</p>
<p>Mauris faucibus varius massa vel vulputate. Praesent ac ligula pretium, viverra eros vitae, consequat metus. Morbi commodo vehicula sem, eget scelerisque ipsum rutrum ut. Maecenas cursus dolor mattis risus dapibus pulvinar. Suspendisse ut dolor nec arcu lobortis molestie.</p>
<p>Donec eros felis, dictum non placerat vitae, sodales in risus. Etiam felis lectus, consectetur quis tempor non, porta a metus. Cras finibus nibh a est semper, eget consequat libero pretium. Pellentesque placerat feugiat enim sit amet sodales. Proin convallis dui eu nibh tincidunt, a posuere dolor sagittis. Ut egestas et eros eu convallis. Integer eros elit, blandit at euismod sit amet, blandit sed velit. Donec dapibus nulla in augue commodo, at efficitur orci dictum. Praesent ultrices accumsan iaculis. Maecenas ut metus a lectus venenatis euismod. Sed auctor, dui efficitur molestie convallis, diam odio faucibus turpis, vitae bibendum ante est non ligula.</p>
<wa-divider></wa-divider>
<div class="wa:post-footer">
<wa-radio-group name="reactions" class="wa:post-reactions">
<wa-radio-button value="applaud"><wa-icon name="hands-clapping" label="Applaud"></wa-icon><small class="wa:count">74</small></wa-radio-button>
<wa-radio-button value="love"><wa-icon name="heart" label="Love"></wa-icon><small class="wa:count">161</small></wa-radio-button>
<wa-radio-button value="laugh"><wa-icon name="face-laugh-beam" label="Laugh"></wa-icon><small class="wa:count">9</small></wa-radio-button>
<wa-radio-button value="cry"><wa-icon name="face-sad-tear" label="Cry"></wa-icon><small class="wa:count">1</small></wa-radio-button>
</wa-radio-group>
<div class="wa:post-actions">
<wa-tooltip content="Save">
<wa-icon-button name="bookmark" label="Save"></wa-icon-button>
</wa-tooltip>
<wa-tooltip content="Share">
<wa-icon-button name="share-from-square" label="Share"></wa-icon-button>
</wa-tooltip>
</div>
</div>
</div>
<section class="wa:post-comments wa:block-flow:2xl">
<h2>Comments</h2>
<article class="wa:comment-composer">
<wa-avatar label="User avatar"></wa-avatar>
<wa-textarea rows="1" placeholder="Add a comment"></wa-textarea>
</article>
<article class="wa:comment">
<wa-avatar image="https://bit.ly/3V9kV7a" label="User avatar"></wa-avatar>
<div class="wa:comment-content">
<div class="wa:comment-bubble">
<div class="wa:comment-details">
<strong>Pedro Pascal</strong>
<small>1d</small>
</div>
<span>You expect me to search the galaxy for the home of this creature and deliver it to a race of enemy sorcerers?</span>
</div>
<div class="wa:comment-actions">
<div class="wa:reaction-like">
<wa-icon-button name="thumbs-up" label="Like"></wa-icon-button>
<small>(3)</small>
</div>
<wa-button variant="text" size="small">Reply</wa-button>
</div>
<article class="wa:comment">
<wa-avatar image="https://bit.ly/3Pb2cUC" label="User avatar"></wa-avatar>
<div class="wa:comment-content">
<div class="wa:comment-bubble">
<div class="wa:comment-details">
<strong>Emily Swallow</strong>
<small>12h</small>
</div>
<span>This is the Way.</span>
</div>
<div class="wa:comment-actions">
<div class="wa:reaction-like">
<wa-icon-button name="thumbs-up" label="Like"></wa-icon-button>
<small>(21)</small>
</div>
<wa-button variant="text" size="small">Reply</wa-button>
</div>
</div>
</article>
</div>
</article>
<article class="wa:comment">
<wa-avatar image="https://bit.ly/3Tq9GWr" label="User avatar"></wa-avatar>
<div class="wa:comment-content">
<div class="wa:comment-bubble">
<div class="wa:comment-details">
<strong>Nick Nolte</strong>
<small>2w</small>
</div>
<span>I have spoken.</span>
</div>
<div class="wa:comment-actions">
<div class="wa:reaction-like">
<wa-icon-button name="thumbs-up" label="Like"></wa-icon-button>
<small>(1)</small>
</div>
<wa-button variant="text" size="small">Reply</wa-button>
</div>
</div>
</article>
</section>
</section>
<section class="wa:blog-recommended-posts">
<h2>You may also like</h2>
<div class="wa:post-list">
<article class="wa:post-link">
<div class="wa:frame:landscape">
<img src="http://bit.ly/49ThK7O" alt="">
</div>
<div class="wa:post-details">
<wa-format-date date="2024-02-16T09:17:00-04:00" month="long" year="numeric"></wa-format-date>
<wa-tag size="small">Arts & Culture</wa-tag>
</div>
<h4>Eget Consequat Libero</h4>
</article>
<article class="wa:post-link">
<div class="wa:frame:landscape">
<img src="https://bit.ly/3wHdFFp" alt="">
</div>
<div class="wa:post-details">
<wa-format-date date="2024-01-16T09:17:00-04:00" month="long" year="numeric"></wa-format-date>
<wa-tag size="small">Design</wa-tag>
</div>
<h4>Sed a Leo Tempus Aute Irure</h4>
</article>
<article class="wa:post-link">
<div class="wa:frame:landscape">
<img src="https://bit.ly/49LxbPx" alt="">
</div>
<div class="wa:post-details">
<wa-format-date date="2023-12-16T09:17:00-04:00" month="long" year="numeric"></wa-format-date>
<wa-tag size="small">Arts & Culture</wa-tag>
</div>
<h4>Ultrices Posuere Cubilia Curae</h4>
</article>
</div>
</section>
</div>

View File

@@ -1,586 +0,0 @@
---
meta:
title: Dashboard
description: TODO
toc: false
---
<style>
:root {
--docs-content-max-width: 80rem;
}
html {
background-color: var(--wa-color-surface-lowered);
}
#menu-toggle,
#sidebar {
display: none;
}
main {
padding: initial;
padding-bottom: var(--wa-space-3xl);
margin: var(--wa-space-xl);
}
.anchor-heading a {
display: none;
}
.preview-container {
container: preview / inline-size;
}
/* strata - support table */
.support-table {
font-size: var(--wa-font-size-s);
}
.support-table th {
padding: var(--wa-space-l);
}
.support-table td {
padding: var(--wa-space-m) var(--wa-space-l);
}
.support-table .desc {
max-width: 30ch;
text-overflow: ellipsis;
overflow: hidden;
white-space: nowrap;
}
.support-table .excerpt {
color: var(--wa-color-text-quiet);
}
.support-table wa-avatar {
--size: var(--wa-font-size-2xl);
}
.support-table wa-card > * {
border-radius: calc(var(--border-radius) - var(--border-width));
}
.wa\:card-title {
font-size: var(--wa-font-size-l);
margin-block-end: 0;
}
.wa\:statistic {
& .wa\:card-title {
color: var(--wa-color-text-quiet);
font-size: var(--wa-font-size-s);
}
& .wa\:value {
font-size: var(--wa-font-size-2xl);
font-weight: var(--wa-font-weight-heavy);
line-height: var(--wa-font-line-height-compact);
& + wa-badge > wa-icon {
opacity: 0.6;
}
}
}
wa-card#glitches::part(body) {
padding-block-end: 0;
}
wa-card.wa\:statistic::part(base) {
justify-content: center;
}
.wa\:box-icon {
display: flex;
align-items: center;
justify-content: center;
background: var(--wa-color-brand-spot);
color: var(--wa-color-brand-text-on-spot);
border-radius: var(--wa-corners-s);
width: 2.5em;
height: 2.5em;
font-size: var(--wa-font-size-l);
}
.wa\:contact {
font-size: var(--wa-font-size-s);
}
wa-card::part(base) {
height: 100%;
}
wa-checkbox[checked] {
--wa-form-controls-value-color: var(--wa-color-text-quiet);
}
caption {
color: var(--wa-color-text-normal);
text-align: left;
margin: var(--wa-space-xl) var(--wa-space-xl) var(--wa-space-l) var(--wa-space-xl);
& h2 {
margin: 0;
}
}
figure {
margin: 0;
margin-inline: calc(var(--padding) * -1);
}
.sparkline {
height: 1em;
transition: all .5s ease;
}
.sparkline .index {
position: relative;
float: left;
width: 1%;
height: 6.25em;
}
.sparkline .index .count {
display: block;
position: absolute;
bottom: 0;
left: 0;
width: 100%;
height: 0;
background: var(--wa-color-brand-spot);
font: 0/0 a;
text-shadow: none;
color: transparent;
}
figcaption {
display: block;
}
</style>
<!-- cSpell:dictionaries lorem-ipsum -->
<div class="preview-container wa:arrange:flex:align-start:nowrap">
<div class="wa:block-flow:l" style="display: flex; flex-direction: column; align-items: center;">
<wa-icon-button name="crown" href="/" style="color: var(--wa-color-brand-spot);"></wa-icon-button>
<wa-divider style="width: 100%;"></wa-divider>
<wa-icon-button name="home"></wa-icon-button>
<wa-icon-button name="calendar"></wa-icon-button>
<wa-icon-button name="envelope"></wa-icon-button>
<wa-icon-button name="chart-simple"></wa-icon-button>
<wa-icon-button name="archive"></wa-icon-button>
<wa-divider style="width: 100%;"></wa-divider>
<wa-icon-button name="gear"></wa-icon-button>
<wa-icon-button name="right-from-bracket"></wa-icon-button>
</div>
<div class="wa:fill_space wa:block-flow:l">
<div class="wa:arrange:aside-end:gap-l">
<div class="wa:arrange:size-m:gap-l">
<wa-card class="wa:block-flow:l wa:statistic">
<div class="wa:arrange:flex:align-start:gap-m:nowrap">
<span class="wa:box-icon">
<wa-icon name="globe"></wa-icon>
</span>
<div>
<h2 class="wa:fill_space wa:card-title">
Population (Zion)
</h2>
<div class="wa:arrange:flex:gap-s">
<span class="wa:value">251,999</span>
<wa-badge variant="danger">-3%&nbsp;<wa-icon name="arrow-trend-down"></wa-icon></wa-badge>
</div>
</div>
</div>
</wa-card>
<wa-card class="wa:block-flow:l wa:statistic">
<div class="wa:arrange:flex:align-start:gap-m:nowrap">
<span class="wa:box-icon">
<wa-icon name="brain-circuit"></wa-icon>
</span>
<div>
<h2 class="wa:fill_space wa:card-title">
Minds Freed
</h2>
<div class="wa:arrange:flex:gap-s">
<span class="wa:value">0.36%</span>
<wa-badge variant="success">+0.02%&nbsp;<wa-icon name="arrow-trend-up"></wa-icon></wa-badge>
</div>
</div>
</div>
</wa-card>
<wa-card class="wa:block-flow:l wa:statistic">
<div class="wa:arrange:flex:align-start:gap-m:nowrap">
<span class="wa:box-icon">
<wa-icon name="robot"></wa-icon>
</span>
<div>
<h2 class="wa:fill_space wa:card-title">
Agents Discovered
</h2>
<div class="wa:arrange:flex:gap-s">
<span class="wa:value">3</span>
<wa-badge variant="neutral">&plusmn;0%&nbsp;<wa-icon name="wave-triangle"></wa-icon></wa-badge>
</div>
</div>
</div>
</wa-card>
<wa-card class="wa:block-flow:l wa:statistic">
<div class="wa:arrange:flex:align-start:gap-m:nowrap">
<span class="wa:box-icon">
<wa-icon name="spaghetti-monster-flying"></wa-icon>
</span>
<div>
<h2 class="wa:fill_space wa:card-title">
Sentinels Controlled
</h2>
<div class="wa:arrange:flex:gap-s">
<span class="wa:value">208</span>
<wa-badge variant="success">+1%&nbsp;<wa-icon name="arrow-trend-up"></wa-icon></wa-badge>
</div>
</div>
</div>
</wa-card>
</div>
<wa-card>
<div class="wa:block-flow:l">
<div class="wa:arrange:flex:gap-xl">
<h2 class="wa:card-title">Daily Tasks</h2>
<wa-progress-bar value="40" class="wa:fill_space" style="--height: 0.5em;"></wa-progress-bar>
</div>
<div class="wa:block-flow:s">
<wa-checkbox style="display:block;">Let go fear, doubt, and disbelief</wa-checkbox>
<wa-checkbox style="display:block;">Walk through the door</wa-checkbox>
<wa-checkbox style="display:block;">Train with Morpheus</wa-checkbox>
</div>
</div>
<div slot="footer">
<a href="#">View completed tasks</a>
</div>
</wa-card>
</div>
<section class="strata support-table">
<wa-card style="--padding: 0; width: 100%;">
<table style="margin-bottom: 0;">
<caption>
<h2 class="wa:card-title">Conversations</h2>
</caption>
<thead>
<tr>
<th><wa-checkbox size="large" style="padding-left: var(--wa-space-s)"><wa-visually-hidden>Check all</wa-visually-hidden></wa-checkbox></th>
<th>Customer</th>
<th>Conversation</th>
<th>Assigned To</th>
<th style="text-align: center;">Status</th>
<th><wa-visually-hidden>Actions</wa-visually-hidden></th>
</tr>
</thead>
<tbody>
<tr>
<td><wa-checkbox size="large" style="padding-left: var(--wa-space-s)"><wa-visually-hidden>Completed</wa-visually-hidden></wa-checkbox></td>
<td>Keanu Reeves</td>
<td class="desc"><span style="font-weight: bold">Am I dead?</span><br><span class="excerpt">Okey dokey... free my mind. Right, no problem, free my mind, free my mind, no problem, right...</span></td>
<td><wa-avatar image="/assets/images/kitchen-sink/avatar-chad.jpg" label="Chad" style="margin-right: var(--wa-space-xs)"></wa-avatar> Chad Stahelski</td>
<td style="text-align: center;"><wa-tag variant="warning" size="small">Pending</wa-tag></td>
<td>
<wa-dropdown>
<wa-button slot="trigger" caret size="small">Action</wa-button>
<wa-menu>
<wa-menu-item>
<wa-icon slot="prefix" name="check" variant="regular"></wa-icon>
Resolved
</wa-menu-item>
<wa-menu-item>
<wa-icon slot="prefix" name="clock" variant="regular"></wa-icon>
Pending
</wa-menu-item>
<wa-menu-item>
<wa-icon slot="prefix" name="arrow-rotate-left" variant="regular"></wa-icon>
Re-open
</wa-menu-item>
<wa-menu-item>
<wa-icon slot="prefix" name="xmark" variant="regular"></wa-icon>
Delete
</wa-menu-item>
</wa-menu>
</wa-dropdown>
</td>
</tr>
<tr>
<td><wa-checkbox size="large" style="padding-left: var(--wa-space-s)"><wa-visually-hidden>Completed</wa-visually-hidden></wa-checkbox></td>
<td>Lawrence Fishburne</td>
<td class="desc"><span style="font-weight: bold">We have a rule</span><br><span class="excerpt">We never free a mind once it's reached a certain age. It's dangerous, the mind has trouble letting go.</span></td>
<td><wa-avatar image="/assets/images/kitchen-sink/avatar-char.jpg" label="Char" style="margin-right: var(--wa-space-xs)"></wa-avatar> Char McCoy</td>
<td style="text-align: center;"><wa-tag variant="success" size="small">Resolved</wa-tag></td>
<td>
<wa-dropdown>
<wa-button slot="trigger" caret size="small">Action</wa-button>
<wa-menu>
<wa-menu-item>
<wa-icon slot="prefix" name="check" variant="regular"></wa-icon>
Resolved
</wa-menu-item>
<wa-menu-item>
<wa-icon slot="prefix" name="clock" variant="regular"></wa-icon>
Pending
</wa-menu-item>
<wa-menu-item>
<wa-icon slot="prefix" name="arrow-rotate-left" variant="regular"></wa-icon>
Re-open
</wa-menu-item>
<wa-menu-item>
<wa-icon slot="prefix" name="xmark" variant="regular"></wa-icon>
Delete
</wa-menu-item>
</wa-menu>
</wa-dropdown>
</td>
</tr>
<tr>
<td><wa-checkbox size="large" style="padding-left: var(--wa-space-s)" checked><wa-visually-hidden>Completed</wa-visually-hidden></wa-checkbox></td>
<td>Carrie-Ann Moss</td>
<td class="desc"><span style="font-weight: bold">Was it the same cat?</span><br><span class="excerpt">A déjà vu is usually a glitch in the Matrix. It happens when they change something.</span></td>
<td><wa-avatar initials="DE" label="Avatar with initials: DE" style="margin-right: var(--wa-space-xs)"></wa-avatar> Debbie Evans</td>
<td style="text-align: center;"><wa-tag variant="warning" size="small">Pending</wa-tag></td>
<td>
<wa-dropdown>
<wa-button slot="trigger" caret size="small">Action</wa-button>
<wa-menu>
<wa-menu-item>
<wa-icon slot="prefix" name="check" variant="regular"></wa-icon>
Resolved
</wa-menu-item>
<wa-menu-item>
<wa-icon slot="prefix" name="clock" variant="regular"></wa-icon>
Pending
</wa-menu-item>
<wa-menu-item>
<wa-icon slot="prefix" name="arrow-rotate-left" variant="regular"></wa-icon>
Re-open
</wa-menu-item>
<wa-menu-item>
<wa-icon slot="prefix" name="xmark" variant="regular"></wa-icon>
Delete
</wa-menu-item>
</wa-menu>
</wa-dropdown>
</td>
</tr>
<tr>
<td><wa-checkbox size="large" style="padding-left: var(--wa-space-s)"><wa-visually-hidden>Completed</wa-visually-hidden></wa-checkbox></td>
<td>Joe Pantoliano</td>
<td class="desc"><span style="font-weight: bold">Ignorance is bliss</span><br><span class="excerpt">Why oh why didn't I take the blue pill?</span></td>
<td></td>
<td style="text-align: center;"><wa-tag variant="danger" size="small">Bounced</wa-tag></td>
<td>
<wa-dropdown>
<wa-button slot="trigger" caret size="small">Action</wa-button>
<wa-menu>
<wa-menu-item>
<wa-icon slot="prefix" name="check" variant="regular"></wa-icon>
Resolved
</wa-menu-item>
<wa-menu-item>
<wa-icon slot="prefix" name="clock" variant="regular"></wa-icon>
Pending
</wa-menu-item>
<wa-menu-item>
<wa-icon slot="prefix" name="arrow-rotate-left" variant="regular"></wa-icon>
Re-open
</wa-menu-item>
<wa-menu-item>
<wa-icon slot="prefix" name="xmark" variant="regular"></wa-icon>
Delete
</wa-menu-item>
</wa-menu>
</wa-dropdown>
</td>
</tr>
<tr>
<td><wa-checkbox size="large" style="padding-left: var(--wa-space-s)"><wa-visually-hidden>Completed</wa-visually-hidden></wa-checkbox></td>
<td>Hugo Weaving</td>
<td class="desc"><span style="font-weight: bold">I'd like to share a revelation</span><br><span class="excerpt">I need the codes, I have to get inside Zion and you have to tell me how.</span></td>
<td><wa-avatar image="/assets/images/kitchen-sink/avatar-dara.jpg" label="Dara" style="margin-right: var(--wa-space-xs)"></wa-avatar> Dara Prescott</td>
<td style="text-align: center;"><wa-tag variant="neutral" size="small">Expired</wa-tag></td>
<td>
<wa-dropdown>
<wa-button slot="trigger" caret size="small">Action</wa-button>
<wa-menu>
<wa-menu-item>
<wa-icon slot="prefix" name="check" variant="regular"></wa-icon>
Resolved
</wa-menu-item>
<wa-menu-item>
<wa-icon slot="prefix" name="clock" variant="regular"></wa-icon>
Pending
</wa-menu-item>
<wa-menu-item>
<wa-icon slot="prefix" name="arrow-rotate-left" variant="regular"></wa-icon>
Re-open
</wa-menu-item>
<wa-menu-item>
<wa-icon slot="prefix" name="xmark" variant="regular"></wa-icon>
Delete
</wa-menu-item>
</wa-menu>
</wa-dropdown>
</td>
</tr>
</tbody>
</table>
</wa-card>
</section>
<div class="wa:arrange:gap-l">
<wa-card id="glitches">
<div class="wa:block-flow:l">
<div class="wa:arrange:flex:justify-space_between">
<h2 class="wa:card-title">Glitches</h2>
<small style="color: var(--wa-color-text-quiet);">March 31, 1999</small>
</div>
<figure>
<span class="sparkline">
<span class="index"><span class="count" style="height: 27%;">(60,</span> </span>
<span class="index"><span class="count" style="height: 92%;">220,</span> </span>
<span class="index"><span class="count" style="height: 62%;">140,</span> </span>
<span class="index"><span class="count" style="height: 35%;">80,</span> </span>
<span class="index"><span class="count" style="height: 49%;">110,</span> </span>
<span class="index"><span class="count" style="height: 40%;">90,</span> </span>
<span class="index"><span class="count" style="height: 80%;">180,</span> </span>
<span class="index"><span class="count" style="height: 62%;">140,</span> </span>
<span class="index"><span class="count" style="height: 53%;">120,</span> </span>
<span class="index"><span class="count" style="height: 71%;">160,</span> </span>
<span class="index"><span class="count" style="height: 78%;">175,</span> </span>
<span class="index"><span class="count" style="height: 21%;">225,</span> </span>
<span class="index"><span class="count" style="height: 78%;">175,</span> </span>
<span class="index"><span class="count" style="height: 49%;">110,</span> </span>
<span class="index"><span class="count" style="height: 40%;">90,</span> </span>
<span class="index"><span class="count" style="height: 12%;">180,</span> </span>
<span class="index"><span class="count" style="height: 62%;">140,</span> </span>
<span class="index"><span class="count" style="height: 53%;">120,</span> </span>
<span class="index"><span class="count" style="height: 21%;">220,</span> </span>
<span class="index"><span class="count" style="height: 62%;">140,</span> </span>
<span class="index"><span class="count" style="height: 35%;">80,</span> </span>
<span class="index"><span class="count" style="height: 49%;">110,</span> </span>
<span class="index"><span class="count" style="height: 40%;">90,</span> </span>
<span class="index"><span class="count" style="height: 56%;">220,</span> </span>
<span class="index"><span class="count" style="height: 62%;">140,</span> </span>
<span class="index"><span class="count" style="height: 35%;">80,</span> </span>
<span class="index"><span class="count" style="height: 49%;">110,</span> </span>
<span class="index"><span class="count" style="height: 40%;">90,</span> </span>
<span class="index"><span class="count" style="height: 80%;">180,</span> </span>
<span class="index"><span class="count" style="height: 62%;">140,</span> </span>
<span class="index"><span class="count" style="height: 53%;">120,</span> </span>
<span class="index"><span class="count" style="height: 71%;">160,</span> </span>
<span class="index"><span class="count" style="height: 69%;">175,</span> </span>
<span class="index"><span class="count" style="height: 99%;">225,</span> </span>
<span class="index"><span class="count" style="height: 49%;">175,</span> </span>
<span class="index"><span class="count" style="height: 49%;">110,</span> </span>
<span class="index"><span class="count" style="height: 40%;">90,</span> </span>
<span class="index"><span class="count" style="height: 80%;">180,</span> </span>
<span class="index"><span class="count" style="height: 62%;">140,</span> </span>
<span class="index"><span class="count" style="height: 53%;">120,</span> </span>
<span class="index"><span class="count" style="height: 27%;">(60,</span> </span>
<span class="index"><span class="count" style="height: 77%;">220,</span> </span>
<span class="index"><span class="count" style="height: 62%;">140,</span> </span>
<span class="index"><span class="count" style="height: 35%;">80,</span> </span>
<span class="index"><span class="count" style="height: 49%;">110,</span> </span>
<span class="index"><span class="count" style="height: 49%;">110,</span> </span>
<span class="index"><span class="count" style="height: 40%;">90,</span> </span>
<span class="index"><span class="count" style="height: 80%;">180,</span> </span>
<span class="index"><span class="count" style="height: 62%;">140,</span> </span>
<span class="index"><span class="count" style="height: 53%;">120,</span> </span>
<span class="index"><span class="count" style="height: 71%;">160,</span> </span>
<span class="index"><span class="count" style="height: 38%;">175,</span> </span>
<span class="index"><span class="count" style="height: 85%;">225,</span> </span>
<span class="index"><span class="count" style="height: 78%;">175,</span> </span>
<span class="index"><span class="count" style="height: 49%;">110,</span> </span>
<span class="index"><span class="count" style="height: 40%;">90,</span> </span>
<span class="index"><span class="count" style="height: 80%;">180,</span> </span>
<span class="index"><span class="count" style="height: 62%;">140,</span> </span>
<span class="index"><span class="count" style="height: 53%;">120,</span> </span>
<span class="index"><span class="count" style="height: 92%;">220,</span> </span>
<span class="index"><span class="count" style="height: 62%;">140,</span> </span>
<span class="index"><span class="count" style="height: 35%;">80,</span> </span>
<span class="index"><span class="count" style="height: 49%;">110,</span> </span>
<span class="index"><span class="count" style="height: 40%;">90,</span> </span>
<span class="index"><span class="count" style="height: 92%;">220,</span> </span>
<span class="index"><span class="count" style="height: 62%;">140,</span> </span>
<span class="index"><span class="count" style="height: 35%;">80,</span> </span>
<span class="index"><span class="count" style="height: 46%;">110,</span> </span>
<span class="index"><span class="count" style="height: 40%;">90,</span> </span>
<span class="index"><span class="count" style="height: 80%;">180,</span> </span>
<span class="index"><span class="count" style="height: 62%;">140,</span> </span>
<span class="index"><span class="count" style="height: 53%;">120,</span> </span>
<span class="index"><span class="count" style="height: 71%;">160,</span> </span>
<span class="index"><span class="count" style="height: 60%;">175,</span> </span>
<span class="index"><span class="count" style="height: 100%;">225,</span> </span>
<span class="index"><span class="count" style="height: 78%;">175,</span> </span>
<span class="index"><span class="count" style="height: 49%;">110,</span> </span>
<span class="index"><span class="count" style="height: 40%;">90,</span> </span>
<span class="index"><span class="count" style="height: 60%;">180,</span> </span>
<span class="index"><span class="count" style="height: 62%;">140,</span> </span>
<span class="index"><span class="count" style="height: 53%;">120,</span> </span>
<span class="index"><span class="count" style="height: 27%;">(60,</span> </span>
<span class="index"><span class="count" style="height: 8%;">220,</span> </span>
<span class="index"><span class="count" style="height: 40%;">90,</span> </span>
<span class="index"><span class="count" style="height: 35%;">80,</span> </span>
<span class="index"><span class="count" style="height: 56%;">125)</span> </span>
<span class="index"><span class="count" style="height: 53%;">120,</span> </span>
<span class="index"><span class="count" style="height: 27%;">(60,</span> </span>
<span class="index"><span class="count" style="height: 56%;">220,</span> </span>
<span class="index"><span class="count" style="height: 40%;">90,</span> </span>
<span class="index"><span class="count" style="height: 62%;">140,</span> </span>
<span class="index"><span class="count" style="height: 53%;">120,</span> </span>
<span class="index"><span class="count" style="height: 27%;">(60,</span> </span>
<span class="index"><span class="count" style="height: 78%;">220,</span> </span>
<span class="index"><span class="count" style="height: 40%;">90,</span> </span>
<span class="index"><span class="count" style="height: 35%;">80,</span> </span>
<span class="index"><span class="count" style="height: 53%;">120,</span> </span>
<span class="index"><span class="count" style="height: 27%;">(60,</span> </span>
<span class="index"><span class="count" style="height: 25%;">220,</span> </span>
<span class="index"><span class="count" style="height: 35%;">80,</span> </span>
</span>
</figure>
</div>
<div slot="footer">
<a href="#">View all data</a>
</div>
</wa-card>
<wa-card>
<div class="wa:block-flow:l">
<h2 class="wa:card-title">Recent Contacts</h2>
<div class="wa:arrange:gap-l" style="--wa-grid-size: 20ch;">
<wa-card style="--padding: var(--wa-space-s);">
<div class="wa:arrange:flex:gap-s">
<wa-avatar label="User avatar">
<wa-icon slot="icon" name="user-secret"></wa-icon>
</wa-avatar>
<div class="wa:contact">
<div class="wa:arrange:flex:gap-s">
<strong>Trinity</strong>
</div>
<small><em>Nebuchadnezzar</em></small>
</div>
</div>
</wa-card>
<wa-card style="--padding: var(--wa-space-s);">
<div class="wa:arrange:flex:gap-s">
<wa-avatar label="User avatar">
<wa-icon slot="icon" name="user-tie"></wa-icon>
</wa-avatar>
<div class="wa:contact">
<div class="wa:arrange:flex:gap-s">
<strong>Mr. Rhineheart</strong>
</div>
<small><em>MetaCortex</em></small>
</div>
</div>
</wa-card>
</div>
</div>
<div slot="footer">
<a href="#">View all contacts</a>
</div>
</wa-card>
</div>
</div>
</div>

View File

@@ -1,223 +0,0 @@
---
meta:
title: Ecommerce Product Page
description: TODO
toc: false
---
<style>
:root {
--docs-content-max-width: 72rem;
}
#menu-toggle,
#sidebar {
display: none;
}
main {
padding: initial;
margin: var(--wa-space-xl);
}
.anchor-heading a {
display: none;
}
.preview-container {
container: preview / inline-size;
}
</style>
<!-- cSpell:dictionaries lorem-ipsum -->
<div class="preview-container wa:block-flow:3xl">
<wa-breadcrumb>
<wa-breadcrumb-item>Plants</wa-breadcrumb-item>
<wa-breadcrumb-item>Indoor Plants</wa-breadcrumb-item>
<wa-breadcrumb-item>Orchids</wa-breadcrumb-item>
<wa-breadcrumb-item>Jupiter Moth Orchid</wa-breadcrumb-item>
</wa-breadcrumb>
<div class="wa:container wa:product-overview">
<div class="wa:arrange">
<div class="wa:product-overview:hero">
<img src="https://images.pexels.com/photos/1021386/pexels-photo-1021386.jpeg" alt="">
</div>
<div class="wa:product-overview:description wa:block-flow:2xl">
<div class="wa:product-overview:summary wa:block-flow:s">
<wa-badge>20% Off</wa-badge>
<h1>Jupiter Moth Orchid</h1>
<p class="wa:product-overview:price"><s>$35</s> $28</p>
<div class="wa:product-overview:rating wa:arrange:flex:gap-s">
<wa-rating label="average stars" readonly precision="0.1" value="4.7"></wa-rating>
<a href="#product-reviews"><small>419 reviews</small></a>
</div>
<p>Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.</p>
</div>
<div class="wa:product-overview:actions wa:arrange:flex:gap-s">
<wa-input type="number" value="1" min="1" max="10"></wa-input>
<wa-button class="wa:fill_space" variant="brand">Add to cart</wa-button>
</div>
<div class="wa:product-overview:details wa:block-flow:s">
<wa-details summary="Details">
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna
aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.
</wa-details>
<wa-details summary="Care instructions">
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna
aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.
</wa-details>
</div>
</div>
</div>
</div>
<div class="wa:container wa:product-reviews" id="product-reviews">
<wa-tab-group>
<wa-tab slot="nav" panel="reviews">Reviews</wa-tab>
<wa-tab slot="nav" panel="questions">Questions</wa-tab>
<wa-tab-panel name="reviews" style="margin-top:var(--wa-space-s);">
<div class="wa:arrange:aside-start">
<div class="wa:product-reviews:overview wa:block-flow:2xl">
<h2>Ratings and reviews</h2>
<div class="wa:block-flow:s">
<div class="wa:product-reviews:summary wa:arrange:flex:gap-s">
<h3 class="wa:arrange:flex:gap-s">
4.7
<wa-rating label="average stars" readonly precision="0.1" value="4.7"></wa-rating>
</h3>
<small>Based on 419 reviews</small>
</div>
<div class="wa:product-reviews:breakdown">
<ol>
<li class="wa:arrange:flex:gap-s">
<span>5</span>
<wa-icon name="star"></wa-icon>
<wa-progress-bar class="wa:fill_space" value="82"></wa-progress-bar>
<span>340</span>
</li>
<li class="wa:arrange:flex:gap-s">
<span>4</span>
<wa-icon name="star"></wa-icon>
<wa-progress-bar class="wa:fill_space" value="12"></wa-progress-bar>
<span>53</span>
</li>
<li class="wa:arrange:flex:gap-s">
<span>3</span>
<wa-icon name="star"></wa-icon>
<wa-progress-bar class="wa:fill_space" value="6"></wa-progress-bar>
<span>24</span>
</li>
<li class="wa:arrange:flex:gap-s">
<span>2</span>
<wa-icon name="star"></wa-icon>
<wa-progress-bar class="wa:fill_space" value="0"></wa-progress-bar>
<span>0</span>
</li>
<li class="wa:arrange:flex:gap-s">
<span>1</span>
<wa-icon name="star"></wa-icon>
<wa-progress-bar class="wa:fill_space" value="1"></wa-progress-bar>
<span>2</span>
</li>
</ol>
</div>
</div>
<wa-divider></wa-divider>
<div class="wa:block-flow:s">
<h3>Happy with your purchase?</h3>
<p>Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.</p>
<wa-button outline size="small" style="width: 100%;">Write a review</wa-button>
</div>
</div>
<div class="wa:product-reviews:list wa:block-flow:2xl">
<div class="wa:product-reviews:review wa:block-flow:s">
<div class="wa:arrange:flex:gap-s">
<wa-avatar label="User avatar"></wa-avatar>
<div class="wa:product-reviews:attribution">
<div class="wa:arrange:flex:gap-s">
<strong>Cory L.</strong> <wa-tag variant="success" size="small"><wa-icon name="check" style="margin-inline-end:var(--wa-space-2xs);"></wa-icon>Verified</wa-tag>
</div>
<small><wa-format-date month="long" day="numeric" year="numeric"></wa-format-date></small>
</div>
</div>
<wa-rating label="Rating" readonly value="5"></wa-rating>
<p>Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.</p>
</div>
<div class="wa:product-reviews:review wa:block-flow:s">
<div class="wa:arrange:flex:gap-s">
<wa-avatar label="User avatar"></wa-avatar>
<div class="wa:product-reviews:attribution">
<div class="wa:arrange:flex:gap-s">
<strong>Konnor R.</strong> <wa-tag variant="success" size="small"><wa-icon name="check" style="margin-inline-end:var(--wa-space-2xs);"></wa-icon>Verified</wa-tag>
</div>
<small><wa-format-date date="2023-11-16T09:17:00-04:00" month="long" day="numeric" year="numeric"></wa-format-date></small>
</div>
</div>
<wa-rating label="Rating" readonly value="4"></wa-rating>
<p>Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.</p>
</div>
<div class="wa:product-reviews:review wa:block-flow:s">
<div class="wa:arrange:flex:gap-s">
<wa-avatar label="User avatar"></wa-avatar>
<div class="wa:product-reviews:attribution">
<div class="wa:arrange:flex:gap-s">
<strong>Kelsey J.</strong> <wa-tag variant="success" size="small"><wa-icon name="check" style="margin-inline-end:var(--wa-space-2xs);"></wa-icon>Verified</wa-tag>
</div>
<small><wa-format-date date="2023-10-31T09:17:00-04:00" month="long" day="numeric" year="numeric"></wa-format-date></small>
</div>
</div>
<wa-rating label="Rating" readonly value="5"></wa-rating>
<p>Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.</p>
</div>
<div class="wa:product-reviews:review wa:block-flow:s">
<div class="wa:arrange:flex:gap-s">
<wa-avatar label="User avatar"></wa-avatar>
<div class="wa:product-reviews:attribution">
<div class="wa:arrange:flex:gap-s">
<strong>Lindsay M.</strong>
</div>
<small><wa-format-date date="2023-07-03T09:17:00-04:00" month="long" day="numeric" year="numeric"></wa-format-date></small>
</div>
</div>
<wa-rating label="Rating" readonly value="5"></wa-rating>
<p>Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.</p>
</div>
<wa-button outline size="small" style="width: 100%;">Load more reviews</wa-button>
</div>
</div>
</wa-tab-panel>
<wa-tab-panel name="questions">
questions
</wa-tab-panel>
</wa-tab-group>
</div>
<div class="wa:container wa:product-list-simple">
<h2>You may also like</h2>
<div class="wa:product-list-simple:items wa:arrange:by-two:size-s">
<wa-card>
<div class="wa:frame:square" slot="image">
<img src="https://images.pexels.com/photos/4076594/pexels-photo-4076594.jpeg?auto=compress&cs=tinysrgb&w=1260&h=750&dpr=2" alt="">
</div>
Triumph Tulip<br>
<strong>$14</strong><br>
</wa-card>
<wa-card>
<div class="wa:frame:square" slot="image">
<img src="https://images.pexels.com/photos/4994350/pexels-photo-4994350.jpeg?auto=compress&cs=tinysrgb&w=1260&h=750&dpr=2" alt="">
</div>
White Doll's Daisy<br>
<strong>$18</strong>
</wa-card>
<wa-card>
<div class="wa:frame:square" slot="image">
<img src="https://images.pexels.com/photos/2223890/pexels-photo-2223890.jpeg?auto=compress&cs=tinysrgb&w=1260&h=750&dpr=2" alt="">
</div>
Common Poppy<br>
<strong>$32</strong>
</wa-card>
<wa-card>
<div class="wa:frame:square" slot="image">
<img src="https://images.pexels.com/photos/1179026/pexels-photo-1179026.jpeg?auto=compress&cs=tinysrgb&w=1260&h=750&dpr=2" alt="">
</div>
Stargazer Lily<br>
<strong>$39</strong>
</wa-card>
</div>
</div>
</div>

View File

@@ -1,30 +0,0 @@
---
meta:
title: Patterns
description: TBD
toc: false
---
<!-- cSpell:dictionaries lorem-ipsum -->
<style>
.code-preview__preview > *:first-of-type {
/* transform: scale(0.5); */
}
.code-preview {
width: 50%;
}
</style>
# Patterns
## Full demos
- [Ecommerce: Product Page](/experimental/demo_patterns/ecommerce_product_page)
- [Blog: Listing Page](/experimental/demo_patterns/blog_listing)
- [Blog: Post Page](/experimental/demo_patterns/blog_post)
- [Dashboard](/experimental/demo_patterns/dashboard)
## Isolated patterns
- [Blog Post](/experimental/demo_patterns/blog_patterns)

File diff suppressed because it is too large Load Diff

View File

Before

Width:  |  Height:  |  Size: 91 KiB

After

Width:  |  Height:  |  Size: 91 KiB

View File

Before

Width:  |  Height:  |  Size: 69 KiB

After

Width:  |  Height:  |  Size: 69 KiB

View File

Before

Width:  |  Height:  |  Size: 100 KiB

After

Width:  |  Height:  |  Size: 100 KiB

View File

Before

Width:  |  Height:  |  Size: 53 KiB

After

Width:  |  Height:  |  Size: 53 KiB

View File

Before

Width:  |  Height:  |  Size: 175 KiB

After

Width:  |  Height:  |  Size: 175 KiB

View File

Before

Width:  |  Height:  |  Size: 308 B

After

Width:  |  Height:  |  Size: 308 B

View File

Before

Width:  |  Height:  |  Size: 356 B

After

Width:  |  Height:  |  Size: 356 B

View File

Before

Width:  |  Height:  |  Size: 786 B

After

Width:  |  Height:  |  Size: 786 B

View File

Before

Width:  |  Height:  |  Size: 607 B

After

Width:  |  Height:  |  Size: 607 B

View File

Before

Width:  |  Height:  |  Size: 305 B

After

Width:  |  Height:  |  Size: 305 B

View File

Before

Width:  |  Height:  |  Size: 872 B

After

Width:  |  Height:  |  Size: 872 B

View File

Before

Width:  |  Height:  |  Size: 465 B

After

Width:  |  Height:  |  Size: 465 B

View File

Before

Width:  |  Height:  |  Size: 770 B

After

Width:  |  Height:  |  Size: 770 B

View File

Before

Width:  |  Height:  |  Size: 330 B

After

Width:  |  Height:  |  Size: 330 B

View File

Before

Width:  |  Height:  |  Size: 754 B

After

Width:  |  Height:  |  Size: 754 B

View File

Before

Width:  |  Height:  |  Size: 812 B

After

Width:  |  Height:  |  Size: 812 B

View File

Before

Width:  |  Height:  |  Size: 322 B

After

Width:  |  Height:  |  Size: 322 B

View File

Before

Width:  |  Height:  |  Size: 1.2 KiB

After

Width:  |  Height:  |  Size: 1.2 KiB

View File

Before

Width:  |  Height:  |  Size: 465 B

After

Width:  |  Height:  |  Size: 465 B

View File

Before

Width:  |  Height:  |  Size: 321 B

After

Width:  |  Height:  |  Size: 321 B

View File

Before

Width:  |  Height:  |  Size: 289 B

After

Width:  |  Height:  |  Size: 289 B

View File

Before

Width:  |  Height:  |  Size: 267 B

After

Width:  |  Height:  |  Size: 267 B

View File

Before

Width:  |  Height:  |  Size: 460 B

After

Width:  |  Height:  |  Size: 460 B

View File

Before

Width:  |  Height:  |  Size: 333 B

After

Width:  |  Height:  |  Size: 333 B

View File

Before

Width:  |  Height:  |  Size: 305 B

After

Width:  |  Height:  |  Size: 305 B

View File

Before

Width:  |  Height:  |  Size: 337 B

After

Width:  |  Height:  |  Size: 337 B

View File

Before

Width:  |  Height:  |  Size: 383 B

After

Width:  |  Height:  |  Size: 383 B

View File

Before

Width:  |  Height:  |  Size: 272 B

After

Width:  |  Height:  |  Size: 272 B

View File

Before

Width:  |  Height:  |  Size: 421 B

After

Width:  |  Height:  |  Size: 421 B

View File

Before

Width:  |  Height:  |  Size: 525 B

After

Width:  |  Height:  |  Size: 525 B

View File

Before

Width:  |  Height:  |  Size: 1003 B

After

Width:  |  Height:  |  Size: 1003 B

View File

Before

Width:  |  Height:  |  Size: 619 B

After

Width:  |  Height:  |  Size: 619 B

View File

Before

Width:  |  Height:  |  Size: 1.6 KiB

After

Width:  |  Height:  |  Size: 1.6 KiB

View File

Before

Width:  |  Height:  |  Size: 1.2 KiB

After

Width:  |  Height:  |  Size: 1.2 KiB

View File

Before

Width:  |  Height:  |  Size: 351 B

After

Width:  |  Height:  |  Size: 351 B

View File

Before

Width:  |  Height:  |  Size: 1.8 KiB

After

Width:  |  Height:  |  Size: 1.8 KiB

View File

Before

Width:  |  Height:  |  Size: 456 B

After

Width:  |  Height:  |  Size: 456 B

View File

Before

Width:  |  Height:  |  Size: 387 B

After

Width:  |  Height:  |  Size: 387 B

View File

Before

Width:  |  Height:  |  Size: 973 B

After

Width:  |  Height:  |  Size: 973 B

View File

Before

Width:  |  Height:  |  Size: 441 B

After

Width:  |  Height:  |  Size: 441 B

View File

Before

Width:  |  Height:  |  Size: 463 B

After

Width:  |  Height:  |  Size: 463 B

View File

Before

Width:  |  Height:  |  Size: 334 B

After

Width:  |  Height:  |  Size: 334 B

View File

Before

Width:  |  Height:  |  Size: 315 B

After

Width:  |  Height:  |  Size: 315 B

View File

Before

Width:  |  Height:  |  Size: 402 B

After

Width:  |  Height:  |  Size: 402 B

View File

Before

Width:  |  Height:  |  Size: 225 B

After

Width:  |  Height:  |  Size: 225 B

View File

Before

Width:  |  Height:  |  Size: 2.2 KiB

After

Width:  |  Height:  |  Size: 2.2 KiB

View File

Before

Width:  |  Height:  |  Size: 423 B

After

Width:  |  Height:  |  Size: 423 B

View File

Before

Width:  |  Height:  |  Size: 789 B

After

Width:  |  Height:  |  Size: 789 B

View File

Before

Width:  |  Height:  |  Size: 360 B

After

Width:  |  Height:  |  Size: 360 B

View File

Before

Width:  |  Height:  |  Size: 267 B

After

Width:  |  Height:  |  Size: 267 B

View File

Before

Width:  |  Height:  |  Size: 998 B

After

Width:  |  Height:  |  Size: 998 B

View File

Before

Width:  |  Height:  |  Size: 399 B

After

Width:  |  Height:  |  Size: 399 B

View File

Before

Width:  |  Height:  |  Size: 574 B

After

Width:  |  Height:  |  Size: 574 B

View File

Before

Width:  |  Height:  |  Size: 454 B

After

Width:  |  Height:  |  Size: 454 B

View File

Before

Width:  |  Height:  |  Size: 919 B

After

Width:  |  Height:  |  Size: 919 B

View File

Before

Width:  |  Height:  |  Size: 375 B

After

Width:  |  Height:  |  Size: 375 B

View File

Before

Width:  |  Height:  |  Size: 427 B

After

Width:  |  Height:  |  Size: 427 B

View File

Before

Width:  |  Height:  |  Size: 308 B

After

Width:  |  Height:  |  Size: 308 B

View File

Before

Width:  |  Height:  |  Size: 443 B

After

Width:  |  Height:  |  Size: 443 B

View File

Before

Width:  |  Height:  |  Size: 492 B

After

Width:  |  Height:  |  Size: 492 B

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