Make smoke tests more deterministic (#2618)

* sync first remote smoke tests

* update smoke test scripts
This commit is contained in:
Fred K. Schott 2022-02-28 21:38:17 -08:00 committed by GitHub
parent a217c6608d
commit 918f1ea4f7
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
336 changed files with 34335 additions and 167 deletions

View file

@ -60,11 +60,31 @@ jobs:
body: >
This PR is auto-generated by a nightly GitHub action.
It should automatically be merged if tests pass.
- name: Mark Pull Request for Auto-Merge
if: steps.createpr.outputs.pull-request-operation == 'created'
uses: peter-evans/enable-pull-request-automerge@v1
smoke-sync:
if: github.repository_owner == 'withastro'
runs-on: ubuntu-latest
steps:
- name: Check out code using Git
uses: actions/checkout@v2
- name: Set Node version to 16
uses: actions/setup-node@v2
with:
token: ${{ secrets.NIGHTLY_PERSONAL_GITHUB_TOKEN }}
pull-request-number: ${{ steps.createpr.outputs.pull-request-number }}
merge-method: squash
node-version: 16
cache: 'yarn'
- name: Install dependencies
run: yarn install --frozen-lockfile --ignore-engines --ignore-scripts
- name: Sync smoke tests
run: node scripts/smoke/sync.js
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Create Pull Request
id: createpr
uses: peter-evans/create-pull-request@v3
with:
token: ${{ secrets.GITHUB_TOKEN }}
commit-message: '[ci] update smoke tests (remote)'
title: '[ci] update smoke tests (remote)'
body: >
This PR is auto-generated by a nightly GitHub action.
It should automatically be merged if tests pass.

View file

@ -28,7 +28,7 @@
"examples/component/demo",
"examples/component/packages/*",
"scripts",
"scripts/smoke/*",
"smoke/*",
"packages/astro/test/fixtures/builtins/packages/*",
"packages/astro/test/fixtures/builtins-polyfillnode",
"packages/astro/test/fixtures/custom-elements/my-component-lib",

View file

@ -1,8 +1,6 @@
/** @file Runs all smoke tests and may add extra smoke-test dependencies to `yarn.lock`. */
// @ts-check
import Zip from 'adm-zip';
import { execa } from 'execa';
import { polyfill } from '@astropub/webapi';
import { fileURLToPath } from 'node:url';
@ -10,126 +8,12 @@ import { promises as fs } from 'node:fs';
polyfill(globalThis, { exclude: 'window document' });
/* Configuration
/* -------------------------------------------------------------------------- */
/** URL directory containing this current script. */
const scriptDir = new URL('./', import.meta.url);
/** URL directory containing the entire project. */
const rootDir = new URL('../../', import.meta.url);
/** URL directory containing the example subdirectories. */
const exampleDir = new URL('examples/', rootDir);
/** URL directory containing the Astro package. */
const astroDir = new URL('packages/astro/', rootDir);
/** GitHub configuration for the external "docs" Astro project. */
// const docGithubConfig = { org: 'withastro', name: 'docs', branch: 'main' };
/** GitHub configuration for the external "astro.build" Astro project. */
// const wwwGithubConfig = { org: 'withastro', name: 'astro.build', branch: 'main' };
/* Application
/* -------------------------------------------------------------------------- */
/** Runs all smoke tests. */
async function run() {
console.log('');
const directories = await getChildDirectories(exampleDir);
// TODO Skipped the docs-main test since it is failing at the moment.
// TODO Skipped the www test since it is failing at the moment.
console.log('🤖', 'Preparing', 'yarn');
await execa('yarn', [], { cwd: fileURLToPath(rootDir), stdio: 'inherit' });
for (const directory of directories) {
console.log('🤖', 'Testing', directory.pathname.split('/').at(-1));
try {
await execa('yarn', ['run', 'build'], { cwd: fileURLToPath(directory), stdio: 'inherit' });
} catch (err) {
console.log(err);
process.exit(1);
}
// Run with the static build too
if (directory.pathname.includes('astro.build')) {
// astro.build uses the static build, so rerunning with the flag actually negates it.
continue;
}
try {
await execa('yarn', ['build', '--', '--experimental-static-build'], { cwd: fileURLToPath(directory), stdout: 'inherit', stderr: 'inherit' });
} catch (err) {
console.log(err);
process.exit(1);
}
console.log();
}
}
/* Functionality
/* -------------------------------------------------------------------------- */
/** Returns the URL to the ZIP of the given GitHub project. */
const getGithubZipURL = (/** @type {GithubOpts} */ opts) => `https://github.com/${opts.org}/${opts.name}/archive/refs/heads/${opts.branch}.zip`;
/** Returns the awaited ZIP Buffer from the given GitHub project. */
const fetchGithubZip = (/** @type {GithubOpts} */ opts) =>
fetch(getGithubZipURL(opts))
.then((response) => response.arrayBuffer())
.then((arrayBuffer) => Buffer.from(arrayBuffer));
/** Downloads a ZIP from the given GitHub project. */
const downloadGithubZip = async (/** @type {GithubOpts} */ opts) => {
/** Expected directory when the zip is downloaded. */
const githubDir = new URL(`${opts.name}-${opts.branch}`, scriptDir);
console.log('🤖', 'Downloading', `${opts.org}/${opts.name}#${opts.branch}`);
const buffer = await fetchGithubZip(opts);
console.log('🤖', 'Extracting', `${opts.org}/${opts.name}#${opts.branch}`);
new Zip(buffer).extractAllTo(fileURLToPath(scriptDir), true);
console.log('🤖', 'Preparing', `${opts.org}/${opts.name}#${opts.branch}`);
const astroPackage = await readDirectoryPackage(astroDir);
const githubPackage = await readDirectoryPackage(githubDir);
if ('astro' in Object(githubPackage.dependencies)) {
githubPackage.dependencies['astro'] = astroPackage.version;
}
if ('astro' in Object(githubPackage.devDependencies)) {
githubPackage.devDependencies['astro'] = astroPackage.version;
}
if ('astro' in Object(githubPackage.peerDependencies)) {
githubPackage.peerDependencies['astro'] = astroPackage.version;
}
await writeDirectoryPackage(githubDir, githubPackage);
return githubDir;
};
/** Returns the parsed package.json of the given directory. */
const readDirectoryPackage = async (/** @type {URL} */ dir) => JSON.parse(await fs.readFile(new URL('package.json', dir + '/'), 'utf-8'));
/** Returns upon completion of writing a package.json to the given directory. */
const writeDirectoryPackage = async (/** @type {URL} */ dir, /** @type {any} */ data) =>
await fs.writeFile(new URL('package.json', dir + '/'), JSON.stringify(data, null, ' ') + '\n');
const smokeDir = new URL('smoke/', rootDir);
/** Returns all child directories of the given directory. */
const getChildDirectories = async (/** @type {URL} */ dir) => {
@ -145,9 +29,40 @@ const getChildDirectories = async (/** @type {URL} */ dir) => {
return dirs;
};
/* Execution
/* -------------------------------------------------------------------------- */
/** Runs all smoke tests. */
async function run() {
console.log('');
run();
const directories = [...await getChildDirectories(exampleDir), ...await getChildDirectories(smokeDir)];
/** @typedef {{ org: string, name: string, branch: string }} GithubOpts */
console.log('🤖', 'Preparing', 'yarn');
await execa('yarn', [], { cwd: fileURLToPath(rootDir), stdio: 'inherit' });
for (const directory of directories) {
console.log('🤖', 'Testing', directory.pathname.split('/').at(-1));
try {
await execa('yarn', ['run', 'build'], { cwd: fileURLToPath(directory), stdio: 'inherit' });
} catch (err) {
console.log(err);
process.exit(1);
}
// Run with the static build too (skip for remote repos)
if (directory.pathname.includes(smokeDir.pathname)) {
continue;
}
try {
await execa('yarn', ['build', '--', '--experimental-static-build'], { cwd: fileURLToPath(directory), stdout: 'inherit', stderr: 'inherit' });
} catch (err) {
console.log(err);
process.exit(1);
}
console.log();
}
}
run();

97
scripts/smoke/sync.js Normal file
View file

@ -0,0 +1,97 @@
/** @file Runs all smoke tests and may add extra smoke-test dependencies to `yarn.lock`. */
// @ts-check
import Zip from 'adm-zip';
import rimraf from 'rimraf';
import { execa } from 'execa';
import { polyfill } from '@astropub/webapi';
import { fileURLToPath } from 'node:url';
import { promises as fs } from 'node:fs';
polyfill(globalThis, { exclude: 'window document' });
/* Configuration
/* -------------------------------------------------------------------------- */
/** URL directory containing this current script. */
// const scriptDir = new URL('./', import.meta.url);
/** URL directory containing the entire project. */
const rootDir = new URL('../../', import.meta.url);
/** URL directory containing the example subdirectories. */
const exampleDir = new URL('examples/', rootDir);
const smokeDir = new URL('smoke/', rootDir);
/** URL directory containing the Astro package. */
const astroDir = new URL('packages/astro/', rootDir);
/** GitHub configuration for the external "docs" Astro project. */
const docGithubConfig = { org: 'withastro', name: 'docs', branch: 'main' };
/** GitHub configuration for the external "astro.build" Astro project. */
const wwwGithubConfig = { org: 'withastro', name: 'astro.build', branch: 'main' };
/* Application
/* -------------------------------------------------------------------------- */
/** Runs all smoke tests. */
async function run() {
await downloadGithubZip(docGithubConfig);
await downloadGithubZip(wwwGithubConfig);
await execa('yarn', [], { cwd: fileURLToPath(rootDir), stdout: 'inherit', stderr: 'inherit' });
}
/* Functionality
/* -------------------------------------------------------------------------- */
/** Returns the URL to the ZIP of the given GitHub project. */
const getGithubZipURL = (/** @type {GithubOpts} */ opts) => `https://github.com/${opts.org}/${opts.name}/archive/refs/heads/${opts.branch}.zip`;
/** Returns the awaited ZIP Buffer from the given GitHub project. */
const fetchGithubZip = (/** @type {GithubOpts} */ opts) =>
fetch(getGithubZipURL(opts))
.then((response) => response.arrayBuffer())
.then((arrayBuffer) => Buffer.from(arrayBuffer));
/** Downloads a ZIP from the given GitHub project. */
const downloadGithubZip = async (/** @type {GithubOpts} */ opts) => {
/** Expected directory when the zip is downloaded. */
const githubDir = new URL(`${opts.name}-${opts.branch}`, smokeDir);
/** Whether the expected directory is already available */
rimraf.sync(fileURLToPath(githubDir));
console.log('🤖', 'Downloading', `${opts.org}/${opts.name}#${opts.branch}`);
const buffer = await fetchGithubZip(opts);
console.log('🤖', 'Extracting', `${opts.org}/${opts.name}#${opts.branch}`);
new Zip(buffer).extractAllTo(fileURLToPath(smokeDir), true);
console.log('🤖', 'Preparing', `${opts.org}/${opts.name}#${opts.branch}`);
const astroPackage = await readDirectoryPackage(astroDir);
const githubPackage = await readDirectoryPackage(githubDir);
if ('astro' in Object(githubPackage.dependencies)) {
githubPackage.dependencies['astro'] = astroPackage.version;
}
if ('astro' in Object(githubPackage.devDependencies)) {
githubPackage.devDependencies['astro'] = astroPackage.version;
}
if ('astro' in Object(githubPackage.peerDependencies)) {
githubPackage.peerDependencies['astro'] = astroPackage.version;
}
await writeDirectoryPackage(githubDir, githubPackage);
rimraf.sync(fileURLToPath(new URL(`yarn.lock`, githubDir)));
rimraf.sync(fileURLToPath(new URL(`package-lock.json`, githubDir)));
};
/** Returns the parsed package.json of the given directory. */
const readDirectoryPackage = async (/** @type {URL} */ dir) => JSON.parse(await fs.readFile(new URL('package.json', dir + '/'), 'utf-8'));
/** Returns upon completion of writing a package.json to the given directory. */
const writeDirectoryPackage = async (/** @type {URL} */ dir, /** @type {any} */ data) =>
await fs.writeFile(new URL('package.json', dir + '/'), JSON.stringify(data, null, ' ') + '\n');
/* Execution
/* -------------------------------------------------------------------------- */
run();
/** @typedef {{ org: string, name: string, branch: string }} GithubOpts */

130
smoke/astro.build-main/.gitignore vendored Normal file
View file

@ -0,0 +1,130 @@
# Logs
logs
*.log
npm-debug.log*
yarn-debug.log*
yarn-error.log*
lerna-debug.log*
.pnpm-debug.log*
# Diagnostic reports (https://nodejs.org/api/report.html)
report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json
# Runtime data
pids
*.pid
*.seed
*.pid.lock
# Directory for instrumented libs generated by jscoverage/JSCover
lib-cov
# Coverage directory used by tools like istanbul
coverage
*.lcov
# nyc test coverage
.nyc_output
# Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files)
.grunt
# Bower dependency directory (https://bower.io/)
bower_components
# node-waf configuration
.lock-wscript
# Compiled binary addons (https://nodejs.org/api/addons.html)
build/Release
# Dependency directories
node_modules/
jspm_packages/
# Snowpack dependency directory (https://snowpack.dev/)
web_modules/
# TypeScript cache
*.tsbuildinfo
# Optional npm cache directory
.npm
# Optional eslint cache
.eslintcache
# Optional stylelint cache
.stylelintcache
# Microbundle cache
.rpt2_cache/
.rts2_cache_cjs/
.rts2_cache_es/
.rts2_cache_umd/
# Optional REPL history
.node_repl_history
# Output of 'npm pack'
*.tgz
# Yarn Integrity file
.yarn-integrity
# dotenv environment variable files
.env
.env.development.local
.env.test.local
.env.production.local
.env.local
# parcel-bundler cache (https://parceljs.org/)
.cache
.parcel-cache
# Next.js build output
.next
out
# Nuxt.js build / generate output
.nuxt
dist
# Gatsby files
.cache/
# Comment in the public line in if your project uses Gatsby and not Next.js
# https://nextjs.org/blog/next-9-1#public-directory-support
# public
# vuepress build output
.vuepress/dist
# vuepress v2.x temp and cache directory
.temp
.cache
# Serverless directories
.serverless/
# FuseBox cache
.fusebox/
# DynamoDB Local files
.dynamodb/
# TernJS port file
.tern-port
# Stores VSCode versions used for testing VSCode extensions
.vscode-test
# yarn v2
.yarn/cache
.yarn/unplugged
.yarn/build-state.yml
.yarn/install-state.gz
.pnp.*
# macOS-specific files
.DS_Store

View file

@ -0,0 +1,5 @@
[![Netlify Status](https://api.netlify.com/api/v1/badges/3442658e-265e-48ac-b3bc-e270853129c8/deploy-status)](https://app.netlify.com/sites/astro-build/deploys)
# [astro.build](https://astro.build)
The source code for [astro.build](https://astro.build), built with [Astro](https://github.com/withastro/astro).

View file

@ -0,0 +1,33 @@
import type { AstroUserConfig } from "astro";
const config: AstroUserConfig = {
buildOptions: {
site: "https://astro.build",
sitemap: true,
},
renderers: [],
markdownOptions: {
render: [
"@astrojs/markdown-remark",
{
remarkPlugins: [
"remark-smartypants",
["remark-autolink-headings", { behavior: "wrap" }],
],
rehypePlugins: [
"rehype-slug",
["rehype-autolink-headings", { behavior: "wrap" }],
],
syntaxHighlight: 'shiki',
},
],
},
vite: {
ssr: {
noExternal: ['smartypants'],
external: ["svgo"],
},
},
};
export default config;

View file

@ -0,0 +1,27 @@
{
"name": "astro.build",
"version": "0.0.1",
"private": true,
"scripts": {
"start": "astro dev --experimental-static-build",
"dev": "astro dev",
"build": "astro build --experimental-static-build",
"preview": "astro preview",
"update-contributors": "node ./update-contributors.mjs"
},
"devDependencies": {
"astro": "0.23.0-next.10",
"astro-icon": "^0.6.0"
},
"volta": {
"node": "14.18.3"
},
"dependencies": {
"date-fns": "^2.28.0",
"quicklink": "^2.2.0",
"rehype-autolink-headings": "^6.1.0",
"remark-autolink-headings": "^7.0.1",
"remark-smartypants": "^2.0.0",
"smartypants": "^0.1.6"
}
}

View file

@ -0,0 +1,4 @@
# Netlify Redirects
/chat https://discord.gg/grF4GTXXYm
/play/* https://play.astro.build/play/:splat 200
/v0.21 /blog/astro-021-release/ 301

View file

@ -0,0 +1,18 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 425 200">
<view id="flame" viewBox="0 0 160 200" />
<view id="name" viewBox="240 78 185 122" />
<path d="M394.5 78v28.8H425v15.6h-30.5V158c0 3.6.1 7.2.5 10.3.8 5.3 4 10.5 8.4 12.5 5.7 2.6 9.7 2.1 21.6 1.7l-2.9 17.2c-.8.4-4 .3-7 .3-7 0-33.4 2.5-38.8-24.7-.9-4.7-.7-9.5-.7-16.9v-35.8H362l.2-15.9h13.4V78zm-51.7 28.7v91.5H324v-91.5zm0-28.7v16.3h-19V78zm-83.6 102.2h48.2l-18 18H240V78h19.2z"/>
<path fill="#324fff" d="M0 80v80a70.3 70.3 0 0040-40z"/>
<path fill="#283198" d="M40 120c5.7 27.3 5.3 47 0 80L0 160z"/>
<path fill="#0ff" d="M40 120v80l40-40a149.9 149.9 0 00-40-40z"/>
<path fill="#324fff" d="M80 0v80S48.3 55.7 40 40z"/>
<path fill="#324fff" d="M40 40v80a84.8 84.8 0 0040-40z"/>
<path fill="#00e8ff" d="M80 80a182 182 0 010 80l-40-40z"/>
<path fill="#283198" d="M80 80v80c17-7.5 31.5-19 40-40-5.9-17-18.1-30.9-40-40z"/>
<path fill="#283198" d="M120 40v80L80 80z"/>
<path fill="#00e8ff" d="M120 120c6.1 27 4.9 53.6 0 80l-40-40z"/>
<path fill="#324fff" d="M120 120v80l40-40c-5.4-15-18.3-27.9-40-40z"/>
<path fill="#324fff" d="M160 80v80l-40-40z"/>
</svg>

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 621 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 29 KiB

View file

@ -0,0 +1 @@
<svg enable-background="new 0 0 341.7 155.3" viewBox="0 0 341.7 155.3" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"><defs><linearGradient id="a" gradientUnits="userSpaceOnUse" x1="27.513" x2="152.027" y1="3.551" y2="63.991"><stop offset=".118" stop-color="#bbbdbf"/><stop offset=".299" stop-color="#f1f1f2"/><stop offset="1" stop-color="#d0d2d3"/></linearGradient><linearGradient id="b" gradientUnits="userSpaceOnUse" x1="95.792" x2="73.994" y1="33.088" y2="105.748"><stop offset="0" stop-color="#bbbdbf"/><stop offset=".475" stop-color="#929497"/><stop offset="1" stop-color="#58595b"/></linearGradient><linearGradient id="c" gradientUnits="userSpaceOnUse" x1="18.417" x2="144.251" y1="64.728" y2="150.269"><stop offset="0" stop-color="#58595b"/><stop offset=".539" stop-color="#929497"/><stop offset="1" stop-color="#58595b"/></linearGradient><linearGradient id="d" gradientUnits="userSpaceOnUse" x1="75.248" x2="24.386" y1="75.009" y2="261.284"><stop offset="0" stop-color="#4377bb"/><stop offset="0" stop-color="#808184"/><stop offset=".475" stop-color="#404041"/><stop offset="1" stop-color="#231f20"/></linearGradient></defs><path d="m169.2 100.9s3.2 3.4 8.1 3.4c3.4 0 6.2-2 6.2-5.4 0-7.8-15.5-5.9-15.5-15.5 0-4.5 4-8.3 9.7-8.3 5.4 0 8.3 3 8.3 3l-1.5 2.6s-2.7-2.7-6.8-2.7c-4 0-6.5 2.6-6.6 5.3 0 7.4 15.5 5.3 15.5 15.5 0 4.6-3.6 8.4-9.4 8.4-6.4 0-9.8-4-9.8-4zm49.1-25.6c8.8 0 15.8 7 15.8 15.9.1 8.8-7 16.1-15.8 16.2h-.1c-8.8-.1-15.9-7.4-15.8-16.2v-.1c.1-8.9 7.1-15.9 15.9-15.8zm-.1 29.3c7.2-.2 12.9-6.1 12.7-13.3v-.1c0-7.4-5.6-13.1-12.6-13.1s-12.7 5.6-12.7 13c0 7.6 5.6 13.4 12.6 13.5zm33.9-28.7h3l-.1 28.3h14.4v2.7h-17.5zm33.9.1h3l-.1 31.1h-3zm23.2 0h10c9.3 0 15.6 5.7 15.6 15.6s-6.3 15.5-15.6 15.5h-10zm9.6 28.4c7.6 0 12.7-4.5 12.8-12.8 0-8.3-5.1-12.8-12.7-12.8h-6.7l-.1 25.6z" fill="#fff"/><path d="m163 35.8s-52.7-39.8-93.6-30.5c-1.2.3-2.5.6-3.6 1-6.3 2-11 5.5-13.9 9.7-.6.8-1 1.7-1.5 2.5l-15.1 25.7 26.1 5.1c10.3 7.4 24.8 10.5 37.2 7.3l46.5 9.1z" fill="#bbbdbf"/><path d="m163 35.8s-52.7-39.8-93.6-30.5c-1.2.3-2.5.6-3.6 1-6.3 2-11 5.5-13.9 9.7-.6.8-1 1.7-1.5 2.5l-15.1 25.7 26.1 5.1c10.3 7.4 24.8 10.5 37.2 7.3l46.5 9.1z" fill="url(#a)" opacity=".29"/><path d="m51.5 35.3c-1.2.3-2.5.6-3.6 1-16.7 5.4-22.4 21-12.8 34.7s30.9 20.4 47.6 15l62.3-20.2s-52.6-39.8-93.5-30.5z" fill="#929497"/><path d="m51.5 35.3c-1.2.3-2.5.6-3.6 1-16.7 5.4-22.4 21-12.8 34.7s30.9 20.4 47.6 15l62.3-20.2s-52.6-39.8-93.5-30.5z" fill="url(#b)" opacity=".34"/><path d="m133.6 80.3c-9.6-13.7-30.9-20.5-47.6-15.1l-62.4 20.2-19.6 35 111.6 19.1 20-35.6c4-7 3.6-15.6-2-23.6z" fill="url(#c)"/><path d="m114 115.2c-9.6-13.7-30.9-20.5-47.6-15.1l-62.4 20.3s52.7 39.8 93.6 30.5c1.2-.3 2.5-.6 3.6-1 16.7-5.4 22.4-21 12.8-34.7z" fill="url(#d)"/></svg>

After

Width:  |  Height:  |  Size: 2.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 479 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.4 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 697 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 34 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 109 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 144 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 30 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 67 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 41 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 376 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 125 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.2 KiB

View file

@ -0,0 +1,11 @@
<svg width="256" height="256" fill="none" xmlns="http://www.w3.org/2000/svg">
<style>
#flame { fill: #FF5D01; }
#a { fill: #000014; }
@media (prefers-color-scheme: dark) {
#a { fill: #fff; }
}
</style>
<path id="a" fill-rule="evenodd" clip-rule="evenodd" d="M163.008 18.929c1.944 2.413 2.935 5.67 4.917 12.181l43.309 142.27a180.277 180.277 0 00-51.778-17.53l-28.198-95.29a3.67 3.67 0 00-7.042.01l-27.857 95.232a180.225 180.225 0 00-52.01 17.557l43.52-142.281c1.99-6.502 2.983-9.752 4.927-12.16a15.999 15.999 0 016.484-4.798c2.872-1.154 6.271-1.154 13.07-1.154h31.085c6.807 0 10.211 0 13.086 1.157a16.004 16.004 0 016.487 4.806z" />
<path id="flame" fill-rule="evenodd" clip-rule="evenodd" d="M168.19 180.151c-7.139 6.105-21.39 10.268-37.804 10.268-20.147 0-37.033-6.272-41.513-14.707-1.602 4.835-1.961 10.367-1.961 13.902 0 0-1.056 17.355 11.015 29.426 0-6.268 5.081-11.349 11.349-11.349 10.743 0 10.731 9.373 10.721 16.977v.679c0 11.542 7.054 21.436 17.086 25.606a23.27 23.27 0 01-2.339-10.2c0-11.008 6.463-15.107 13.974-19.87 5.976-3.79 12.616-8.001 17.192-16.449a31.024 31.024 0 003.743-14.82c0-3.299-.513-6.479-1.463-9.463z" />
</svg>

After

Width:  |  Height:  |  Size: 1.2 KiB

View file

@ -0,0 +1,59 @@
// @ts-nocheck
class Pixel {
static get inputProperties() {
return ['--border-radius', '--border-color', '--pixel-size', '--variant'];
}
paint(ctx, size, styleMap) {
ctx.fillStyle = "black";
const variant = styleMap.get("--variant").toString().trim();
const corner = styleMap.get("--border-radius").toString();
const px = styleMap.get("--pixel-size").toString();
const w = size.width;
const h = size.height;
switch (variant) {
case 'primary': {
ctx.fillRect(corner, 0, w - corner * 2, h);
for (let i = 0; i < Math.round(corner / px); i++) {
let v = px * i;
let x = v;
let y = corner - v;
ctx.fillRect(x, y, px, h - corner * 2 + (v * 2));
}
for (let i = 0; i < Math.round(corner / px); i++) {
let v = px * i;
let x = w - (px * (i + 1));
let y = corner - v;
ctx.fillRect(x, y, px, h - corner * 2 + (v * 2));
}
return;
}
case 'outline': {
// top + right + bottom + left border
ctx.fillRect(corner, 0, w - corner * 2, px);
ctx.fillRect(w - px, corner, px, h - (corner * 2));
ctx.fillRect(corner, h - px, w - corner * 2, px);
ctx.fillRect(0, corner, px, h - (corner * 2));
for (let i = 0; i < Math.round(corner / px) + 1; i++) {
let v = px * i;
let x = v;
let y = corner - v;
ctx.fillRect(x, y, px, px * 2);
ctx.fillRect(x, h - y - (px * 2), px, px * 2);
}
for (let i = 0; i < Math.round(corner / px) + 1; i++) {
let v = px * i;
let x = w - (px * (i + 1));
let y = corner - v;
ctx.fillRect(x, y, px, px * 2);
ctx.fillRect(x, h - y - (px * 2), px, px * 2);
}
return;
}
}
}
}
registerPaint("pixel", Pixel);

View file

@ -0,0 +1,6 @@
# I, for one, welcome our new robotic overlords
User-agent: *
Allow: /
Sitemap: https://astro.build/sitemap.xml

View file

@ -0,0 +1,3 @@
if (window.matchMedia('(hover: hover)').matches) {
import('/scripts/konami.js').then(mod => mod.init());
}

View file

@ -0,0 +1,59 @@
// Aha! These are not analytics at all!
class KonamiCode {
enabled = false;
keys = ["ArrowUp", "ArrowUp", "ArrowDown", "ArrowDown", "ArrowLeft", "ArrowRight", "ArrowLeft", "ArrowRight", "b", "a"];
accepted = [...new Set(this.keys)];
inputs = [];
constructor({ enable, disable }) {
this.enable = enable;
this.disable = disable;
this.handleKey = this.handleKey.bind(this);
document.addEventListener('keydown', this.handleKey);
}
handleKey({ key }) {
if (this.enabled) {
this.reset();
return;
}
if (!this.accepted.includes(key)) return;
if (this.keys[this.inputs.length] === key) {
this.handleInput(key)
} else {
this.reset();
}
}
handleInput(key) {
this.inputs.push(key);
if (this.inputs.length === 10) {
this.handleMatch();
}
}
handleMatch() {
this.enabled = true;
this.enable();
this.inputs = [];
}
reset() {
if (this.enabled) {
this.enabled = false;
this.disable();
}
if (this.inputs.length) {
this.inputs = [];
}
}
}
export function init() {
new KonamiCode({
enable: () => document.body.classList.add('🥚'),
disable: () => document.body.classList.remove('🥚'),
});
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 172 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 31 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 KiB

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,356 @@
/* Document
* ========================================================================== */
/**
* 1. Add border box sizing in all browsers (opinionated).
* 2. Backgrounds do not repeat by default (opinionated).
*/
*,
::before,
::after {
box-sizing: border-box; /* 1 */
background-repeat: no-repeat; /* 2 */
}
/**
* 1. Add text decoration inheritance in all browsers (opinionated).
* 2. Add vertical alignment inheritance in all browsers (opinionated).
*/
::before,
::after {
text-decoration: inherit; /* 1 */
vertical-align: inherit; /* 2 */
}
/**
* 1. Use the default cursor in all browsers (opinionated).
* 2. Change the line height in all browsers (opinionated).
* 3. Breaks words to prevent overflow in all browsers (opinionated).
* 4. Use a 4-space tab width in all browsers (opinionated).
* 5. Remove the grey highlight on links in iOS (opinionated).
* 6. Prevent adjustments of font size after orientation changes in iOS.
*/
:where(:root) {
cursor: default; /* 1 */
line-height: 1.5; /* 2 */
overflow-wrap: break-word; /* 3 */
-moz-tab-size: 4; /* 4 */
tab-size: 4; /* 4 */
-webkit-tap-highlight-color: transparent; /* 5 */
-webkit-text-size-adjust: 100%; /* 6 */
text-size-adjust: 100%; /* 6 */
}
/* Sections
* ========================================================================== */
/**
* Remove the margin in all browsers (opinionated).
*/
:where(body) {
margin: 0;
}
/**
* Correct the font size and margin on `h1` elements within `section` and
* `article` contexts in Chrome, Edge, Firefox, and Safari.
*/
:where(h1) {
font-size: 2em;
margin: 0.67em 0;
}
/* Grouping content
* ========================================================================== */
/**
* Remove the margin on nested lists in Chrome, Edge, and Safari.
*/
:where(dl, ol, ul) :where(dl, ol, ul) {
margin: 0;
}
/**
* 1. Correct the inheritance of border color in Firefox.
* 2. Add the correct box sizing in Firefox.
*/
:where(hr) {
color: inherit; /* 1 */
height: 0; /* 2 */
}
/**
* Remove the list style on navigation lists in all browsers (opinionated).
*/
:where(nav) :where(ol, ul) {
list-style-type: none;
padding: 0;
}
/**
* Prevent VoiceOver from ignoring list semantics in Safari (opinionated).
*/
:where(nav li)::before {
content: "\200B";
float: left;
}
/**
* 1. Correct the inheritance and scaling of font size in all browsers.
* 2. Correct the odd `em` font sizing in all browsers.
* 3. Prevent overflow of the container in all browsers (opinionated).
*/
:where(pre) {
font-family: monospace, monospace; /* 1 */
font-size: 1em; /* 2 */
overflow: auto; /* 3 */
}
/* Text-level semantics
* ========================================================================== */
/**
* Add the correct text decoration in Safari.
*/
:where(abbr[title]) {
text-decoration: underline;
text-decoration: underline dotted;
}
/**
* Add the correct font weight in Chrome, Edge, and Safari.
*/
:where(b, strong) {
font-weight: bolder;
}
/**
* 1. Correct the inheritance and scaling of font size in all browsers.
* 2. Correct the odd `em` font sizing in all browsers.
*/
:where(code, kbd, samp) {
font-family: monospace, monospace; /* 1 */
font-size: 1em; /* 2 */
}
/**
* Add the correct font size in all browsers.
*/
:where(small) {
font-size: 80%;
}
/* Embedded content
* ========================================================================== */
/*
* Change the alignment on media elements in all browsers (opinionated).
*/
:where(audio, canvas, iframe, img, svg, video) {
vertical-align: middle;
}
/**
* Remove the border on iframes in all browsers (opinionated).
*/
:where(iframe) {
border-style: none;
}
/**
* Change the fill color to match the text color in all browsers (opinionated).
*/
:where(svg:not([fill])) {
fill: currentColor;
}
/* Tabular data
* ========================================================================== */
/**
* 1. Collapse border spacing in all browsers (opinionated).
* 2. Correct table border color in Chrome, Edge, and Safari.
* 3. Remove text indentation from table contents in Chrome, Edge, and Safari.
*/
:where(table) {
border-collapse: collapse; /* 1 */
border-color: currentColor; /* 2 */
text-indent: 0; /* 3 */
}
/* Forms
* ========================================================================== */
/**
* Remove the margin on controls in Safari.
*/
:where(button, input, select) {
margin: 0;
}
/**
* Correct the inability to style buttons in iOS and Safari.
*/
:where(button, [type="button" i], [type="reset" i], [type="submit" i]) {
-webkit-appearance: button;
}
/**
* Change the inconsistent appearance in all browsers (opinionated).
*/
:where(fieldset) {
border: 1px solid #a0a0a0;
}
/**
* Add the correct vertical alignment in Chrome, Edge, and Firefox.
*/
:where(progress) {
vertical-align: baseline;
}
/**
* 1. Remove the margin in Firefox and Safari.
* 3. Change the resize direction in all browsers (opinionated).
*/
:where(textarea) {
margin: 0; /* 1 */
resize: vertical; /* 3 */
}
/**
* 1. Correct the odd appearance in Chrome, Edge, and Safari.
* 2. Correct the outline style in Safari.
*/
:where([type="search" i]) {
-webkit-appearance: textfield; /* 1 */
outline-offset: -2px; /* 2 */
}
/**
* Correct the cursor style of increment and decrement buttons in Safari.
*/
::-webkit-inner-spin-button,
::-webkit-outer-spin-button {
height: auto;
}
/**
* Correct the text style of placeholders in Chrome, Edge, and Safari.
*/
::-webkit-input-placeholder {
color: inherit;
opacity: 0.54;
}
/**
* Remove the inner padding in Chrome, Edge, and Safari on macOS.
*/
::-webkit-search-decoration {
-webkit-appearance: none;
}
/**
* 1. Correct the inability to style upload buttons in iOS and Safari.
* 2. Change font properties to `inherit` in Safari.
*/
::-webkit-file-upload-button {
-webkit-appearance: button; /* 1 */
font: inherit; /* 2 */
}
/* Interactive
* ========================================================================== */
/*
* Add the correct styles in Safari.
*/
:where(dialog) {
background-color: white;
border: solid;
color: black;
height: -moz-fit-content;
height: fit-content;
left: 0;
margin: auto;
padding: 1em;
position: absolute;
right: 0;
width: -moz-fit-content;
width: fit-content;
}
:where(dialog:not([open])) {
display: none;
}
/*
* Add the correct display in Safari.
*/
:where(details > summary:first-of-type) {
display: list-item;
}
/* Accessibility
* ========================================================================== */
/**
* Change the cursor on busy elements in all browsers (opinionated).
*/
:where([aria-busy="true" i]) {
cursor: progress;
}
/*
* Change the cursor on disabled, not-editable, or otherwise
* inoperable elements in all browsers (opinionated).
*/
:where([aria-disabled="true" i], [disabled]) {
cursor: not-allowed;
}
/*
* Change the display on visually hidden accessible elements
* in all browsers (opinionated).
*/
:where([aria-hidden="false" i][hidden]) {
display: initial;
}
:where([aria-hidden="false" i][hidden]:not(:focus)) {
clip: rect(0, 0, 0, 0);
position: absolute;
}

View file

@ -0,0 +1,62 @@
---
import { Sprite } from 'astro-icon';
const { icon, href, title, always = false } = Astro.props;
---
<a class={always ? 'always' : undefined} href={href} label={title} rel="noopener noreferrer">
{icon && <Sprite aria-hidden="true" name={icon} size="1.5em" />}
<span><slot /></span>
<Sprite aria-hidden="true" class="arrow" pack="mdi" name="arrow-right" size="1.5em" />
</a>
<style>
a {
display: flex;
align-items: center;
justify-content: center;
text-decoration: none;
margin-right: -1.5em;
transition: transform 200ms cubic-bezier(0.23, 1, 0.320, 1);
color: var(--color-dusk);
}
a > :global(* + *) {
margin-left: 0.5rem;
}
a > span {
font-family: var(--font-display);
font-size: var(--size-500);
}
.arrow {
opacity: 0;
transform: translateX(-0.5em);
margin-left: 0.25em;
transition: transform 200ms cubic-bezier(0.23, 1, 0.320, 1);
transition-property: transform, opacity;
}
a:hover,
a:focus {
transform: translateX(-0.25em);
color: var(--color-purple);
}
a:active {
color: var(--color-blue);
}
a:hover .arrow,
a:focus .arrow {
opacity: 1;
transform: translateX(0em);
}
.always .arrow {
opacity: 1;
transform: translateX(0);
}
.always:hover,
.always:focus {
transform: translateX(0);
}
.always:hover .arrow,
.always:focus .arrow {
transform: translateX(0.25em);
}
</style>

View file

@ -0,0 +1,57 @@
---
import { Sprite } from 'astro-icon';
import { mentions } from '../mentions.ts';
const { name } = Astro.props;
const author = mentions[name];
if (!author) {
throw new Error(`Could not find author "${name}"!`)
}
---
<span class="author">
<img aria-hidden="true" src={author.avatar} alt={author.name} width="32" height="32" loading="lazy">
<span>{author.name}</span>
<a href={author.twitter} title={`Follow ${author.name} on Twitter`}>
<span>
<Sprite role="img" pack="mdi" name="twitter" width="20" height="20" />
</span>
</a>
</span>
<style>
.author {
display: flex;
flex-direction: row;
align-items: center;
font: inherit;
}
img {
--size: 2rem;
display: block;
width: var(--size);
height: var(--size);
border-radius: 50%;
overflow: hidden;
margin-right: 0.5em;
}
a, a > span {
--size: 1.5rem;
width: var(--size);
height: var(--size);
display: flex;
align-items: center;
justify-content: center;
color: inherit;
}
a {
margin-left: 0.25em;
color: var(--color-dusk);
opacity: 0.7;
}
a:hover,
a:focus {
color: var(--color-dusk);
opacity: 1;
}
</style>

View file

@ -0,0 +1,11 @@
---
const { href, items = [] } = Astro.props;
const Tag = href ? 'a' : 'div';
---
<not-marquee role="marquee" aria-labelledby="not-marquee-label">
<input id="marquee-pause" type="checkbox" aria-label="Toggle marquee">
<Tag {href} class="track">
{Array.from({ length: 6 }, (_, i) => <span class="group" style={`--i: ${i};`} id={i === 0 ? 'not-marquee-label' : undefined} aria-hidden={i > 0 ? 'true' : undefined}>{items.map(item => <span>{item}</span>)}</span>)}
</Tag>
</not-marquee>

View file

@ -0,0 +1,62 @@
---
export interface Props {
title: string;
description: string;
canonicalURL: URL | string,
image?: string;
}
const { title, description, canonicalURL } = Astro.props;
const image = new URL(Astro.props.image || './social.png', Astro.site);
---
<!-- Blocking script -->
<script>
const marqueePaused = (localStorage.getItem('astro:marquee-paused') || 'false') === 'true';
window.addEventListener('DOMContentLoaded', () => {
const checkbox = document.querySelector('#marquee-pause');
if(checkbox) {
checkbox.checked = marqueePaused;
document.body.classList.add('js');
}
});
if ('paintWorklet' in CSS) {
CSS.paintWorklet.addModule('/pixel.worklet.js');
}
</script>
<!-- Global Metadata -->
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width" />
<meta name="theme-color" content="#8D46E7">
<link rel="icon" type="image/svg+xml" href="/favicon.svg">
<link rel="mask-icon" href="/favicon.svg" color="#8D46E7">
<link rel="sitemap" href="/sitemap.xml">
<link rel="alternate" type="application/rss+xml" href="/rss.xml" title="RSS" />
<!-- Preload -->
<link rel="preload" as="style" href="/styles/sanitize.css">
<link rel="preload" as="style" href="/styles/main.css">
<!-- Primary Meta Tags -->
<title>{title}</title>
<meta name="title" content={title} />
<meta name="description" content={description} />
<link rel="canonical" href={canonicalURL}/>
<!-- Open Graph / Facebook -->
<meta property="og:type" content="website" />
<meta property="og:url" content={canonicalURL} />
<meta property="og:title" content={title} />
<meta property="og:description" content={description} />
<meta property="og:image" content={image} />
<!-- Twitter -->
<meta property="twitter:card" content="summary_large_image" />
<meta property="twitter:url" content={canonicalURL} />
<meta property="twitter:title" content={title} />
<meta property="twitter:description" content={description} />
<meta property="twitter:image" content={image} />
<!-- Assets -->
<link rel="stylesheet" href="/styles/sanitize.css" />
<link rel="stylesheet" href="/styles/main.css" />

View file

@ -0,0 +1,26 @@
---
import { Sprite } from 'astro-icon';
import Header from '../components/Header.astro';
import Scripts from '../components/Scripts.astro';
const { wrapper: Wrapper = Fragment, container = false } = Astro.props;
---
<Sprite.Provider>
<Header>
<slot name="nav" />
</Header>
<div id="root">
<Wrapper>
<slot name="start" />
<main id="content" class:list={{ container }}>
<slot />
</main>
</Wrapper>
<slot name="footer" />
</div>
</Sprite.Provider>
<Scripts />

View file

@ -0,0 +1,48 @@
<section class="quote">
<div class="logo">
<slot name="logo" />
</div>
<blockquote class="container">
<p class=""><slot name="quote" /></p>
<cite class="head-sm"><slot name="cite" /></cite>
</blockquote>
</section>
<style>
.quote {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
position: relative;
color: var(--color, var(--color-dusk));
margin-top: 4rem;
margin-bottom: 4rem;
}
blockquote {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
text-align: center;
font-weight: bold;
padding: 2rem 0 0;
font-size: inherit;
max-width: 36rem;
}
cite {
margin-top: 1rem;
font-style: normal;
font-size: var(--size-500);
}
cite > :global(*) {
font: inherit;
}
.logo > :global(a) {
padding: 1rem;
display: inline-flex;
}
</style>

View file

@ -0,0 +1,7 @@
---
const { cols = 2 } = Astro.props;
---
<ul class="checklist" style={`--cols: ${cols};`} role="list">
<slot />
</ul>

View file

@ -0,0 +1,43 @@
---
import { Sprite } from 'astro-icon'
const { href } = Astro.props;
const Wrapper = href ? 'a' : Fragment;
const wrapperProps = href ? { href, target: '_blank', rel: 'noopener noreferrer' } : {};
---
<li class="checklist-item" role="listitem">
<Wrapper {...wrapperProps}>
<span class="checklist-marker">
<slot name="icon" />
</span>
<span class="checklist-text"><slot /></span>
<Sprite aria-hidden="true" class="arrow" pack="mdi" name="arrow-right" size="1em" />
</Wrapper>
</li>
<style>
a {
display: inline-flex;
align-items: center;
color: inherit;
text-decoration: none;
}
.checklist-marker {
transition: transform 200ms cubic-bezier(0.23, 1, 0.320, 1);
transition-property: transform, background, color;
}
a:is(:hover, :focus) .checklist-marker {
color: var(--color-dawn);
background: var(--color-purple);
transform: scale(1.10);
}
a .arrow {
opacity: 0;
transition: transform 200ms cubic-bezier(0.23, 1, 0.320, 1);
transition-property: transform, opacity;
}
a:is(:hover, :focus) .arrow {
transform: translateX(0.25em);
opacity: 1;
}
</style>

View file

@ -0,0 +1,61 @@
---
const {obj, size, type} = Astro.props;
let color, label;
switch (type) {
case 'staff':
color= 'var(--color-red)';
label= 'Staff';
break;
case 'l3':
color= 'var(--color-purple)';
label= 'Core';
break;
case 'l2':
color= 'var(--color-blue)';
label= 'Maintainer';
break;
default:
color = 'none';
break;
}
---
<a class="avatar" title={`@${obj.login}`} href={obj.html_url}>
<div class="avatar-image-border" style={`
box-shadow:
0px 0px 4px 0px ${color};`}>
<div class="avatar-image" style={`
width: ${size}px;
height: ${size}px;
background-image: url("${obj.avatar_url}"); `}>
</div>
</div>
{label && <div class="badge" style={`background-color: ${color}`}>{label}</div>}
</a>
<style>
.avatar {
position: relative;
display: block;
}
.avatar-image-border {
overflow: hidden;
border-radius: 50%;
}
.avatar-image {
background-size: 100% 100%;
background-position: center center;
transition: background-size 400ms ease-out, filter 200ms ease-out;
}
.badge {
position: absolute;
right: -6px;
bottom: 0;
border-radius: 6px;
background-color: limegreen;
padding: 2px 6px;
font-size: 11px;
color: white;
font-weight: bold;
}
</style>

View file

@ -0,0 +1,25 @@
---
import ContributorAvatar from './ContributorAvatar.astro';
import ArrowLink from '../components/ArrowLink.astro';
import contributors from '../data/contributors.json';
const {staff, l3, l2, l1} = contributors;
---
<ul class="avatar-list">
{staff.map(obj => <li><ContributorAvatar size={72} obj={obj} type="staff" /></li>)}
{l3.map(obj => <li><ContributorAvatar size={72} obj={obj} type="l3" /></li>)}
{l2.map(obj => <li><ContributorAvatar size={72} obj={obj} type="l2" /></li>)}
{l1.map(obj => <li><ContributorAvatar size={72} obj={obj} highlight="255, 255, 255" /></li>)}
<ArrowLink always href="https://github.com/withastro/astro/graphs/contributors">See all 200+ contributors</ArrowLink>
</ul>
<style>
.avatar-list {
list-style: none;
display: flex;
flex-wrap: wrap;
justify-content: center;
gap: 2.5rem;
margin: 2.5rem -2.5rem;
}
</style>

View file

@ -0,0 +1,21 @@
---
import { parse, startOfDay, intlFormat, formatISO } from 'date-fns';
const { value } = Astro.props;
let date;
if (typeof value === 'string') {
date = parse(value, 'MMMM d, yyyy', new Date());
} else {
date = value;
}
const dateISO = formatISO(startOfDay(date), { representation: 'date' });
const dateFormatted = intlFormat(date, {
year: 'numeric',
month: 'long',
day: 'numeric',
}, {
locale: 'en-US',
})
---
<time datetime={dateISO}>{dateFormatted}</time>

View file

@ -0,0 +1,229 @@
---
import { Sprite } from 'astro-icon';
import Quote from './landing/Quote.astro';
import Grid from './landing/Grid.astro';
import Community from './landing/Community.astro';
import { social } from '../config.ts';
const { quote } = Astro.slots;
const YYYY = new Date().getFullYear();
---
<footer class={!quote ? 'has-card' : ''}>
<div class="background">
<div class="blob" />
<Grid class="footer-grid" />
</div>
{!quote && <Community />}
{quote && (
<Quote color="white" blobs={false}>
<Fragment slot="logo">
<slot name="logo" />
</Fragment>
<Fragment slot="quote"><slot name="quote" /></Fragment>
<Fragment slot="cite"><slot name="cite" /></Fragment>
</Quote>
)}
<div class="container content">
<ul class="links">
<li>&copy; {YYYY} The Astro Technology Company</li>
<li><a href="/about">About</a></li>
<li><a href="/company">We're Hiring!</a></li>
<li><a href="/blog">Blog</a></li>
</ul>
</div>
</footer>
<style>
footer {
overflow: hidden;
--height: clamp(min-content, 75vh, 56rem);
min-height: var(--height);
position: relative;
background: var(--color-tan) linear-gradient(180deg, #E8ADB7, #C776BE, #9039CF, #7D32DE);
}
.has-card {
/* background: var(--color-tan) var(--gradient-pop-3); */
}
footer::before {
content: '';
position: absolute;
top: 0;
right: 0;
bottom: 0;
left: 0;
background: radial-gradient(200% 100% at 50% 100%, #0029FF 0%, #6D39FF 30%, rgba(255, 153, 0, 0) 100%);
}
footer > :global(.quote) {
z-index: 1;
background: none;
margin-bottom: 10vh;
}
.footer-grid {
position: absolute;
bottom: -10% !important;
height: 40% !important;
z-index: 1 !important;
}
.background {
--height: 1350px;
pointer-events: none;
position: absolute;
width: 100vw;
bottom: 0;
margin-top: calc(var(--height) * -0.9);
height: calc(var(--height) * 1.25);
overflow: hidden;
}
.content {
position: relative;
z-index: 1;
color: white;
margin-bottom: 6.5rem;
display: grid;
grid-template-columns: 1fr;
}
.content ul {
list-style: none;
display: flex;
flex-flow: row wrap;
align-items: center;
}
.content a {
color: white;
text-decoration: none;
}
.content a:hover,
.content a:focus,
.content a:active {
text-decoration: underline;
}
ul > li + li {
margin-left: 1rem;
}
ul.main > li:nth-child(2) {
margin-left: auto !important;
}
.logo .logomark {
display: none;
}
.logo .wordmark {
display: block;
margin-left: 1rem;
}
.icon {
transform: scale(0.8);
}
.links {
order: 9;
margin-top: 1rem;
margin-left: 1rem;
font-size: var(--size-400);
justify-content: center;
}
.links > li + li {
margin-left: 1rem;
padding-left: 1rem;
border-left: 1px solid rgba(255, 255, 255, 0.6);
}
.links > li:first-child {
width: 100%;
margin-bottom: 2rem;
text-align: center;
}
.links > li + li:nth-child(2) {
margin-left: 0;
padding-left: 0;
border: none;
}
.content ul.mark {
flex-direction: column;
align-items: flex-start;
max-width: max-content;
margin-left: 1rem;
}
.mark li:first-child {
display: none;
}
.mark > li:nth-child(2) {
margin-left: 0;
margin-top: 1rem;
}
.mark address {
font-style: normal;
display: flex;
}
.mark address p {
font-size: var(--size-400);
opacity: 0.75;
}
.mark address p + p {
margin-left: 1rem;
}
@media (min-width: 52rem) {
.icon {
transform: scale(1);
}
.logo {
padding: 0;
}
.logo .logomark {
display: block;
}
.logo .wordmark {
display: none;
}
.content {
grid-template-columns: 1fr max-content;
}
.content ul.mark {
grid-row: 1 / 3;
margin-top: 0;
margin-left: auto;
padding-right: 2rem;
}
ul.main {
height: 6rem;
}
ul.main > li:nth-child(2) {
margin-left: 2rem !important;
}
.links {
order: initial;
grid-row: 2;
}
.links > li:first-child {
width: initial;
margin-bottom: 0;
}
.links > li + li:nth-child(2) {
margin-left: 1rem;
padding-left: 1rem;
border-left: 1px solid rgba(255, 255, 255, 0.6);
}
.mark {
margin-top: 3rem;
}
.mark li:first-child {
display: block;
}
.mark address {
flex-direction: column;
}
.mark address p + p {
margin-left: 0;
margin-top: 1rem;
}
}
@media (min-width: 64rem) {
.content ul.mark {
padding-right: 6rem;
}
}
</style>

View file

@ -0,0 +1,7 @@
---
import SkipLink from './SkipLink.astro';
// import { getStars } from '../utils.ts';
---
<SkipLink />
<slot />

View file

@ -0,0 +1,58 @@
---
import { Sprite } from 'astro-icon';
import { mentions } from '../mentions.ts';
const { name } = Astro.props;
const mention = mentions[name];
if (!mention) {
throw new Error(`Could not find mentioned user "${name}"!`)
}
---
<span class="mention">
<a href={mention.twitter} title={`Follow ${mention.name} on Twitter`}>
<img aria-hidden="true" alt={mention.name} src={mention.avatar} loading="lazy">
<span>{mention.name}</span>
</a>
</span>
<style>
.mention {
--padding-block: 0.125rem;
--padding-inline: 0.33rem;
display: inline-flex;
align-self: baseline;
white-space: nowrap;
margin: calc(var(--padding-block) * -1) calc(var(--padding-inline) / -2);
background: var(--color-dawn);
text-decoration: none;
border-radius: 2rem;
}
a {
display: flex;
font-family: var(--font-display);
letter-spacing: 0.5px;
font-size: 0.8em;
flex-direction: row;
align-items: center;
padding: var(--padding-block) var(--padding-inline);
text-decoration: none;
color: var(--color-purple);
border-radius: inherit;
box-shadow: var(--shadow-sm);
}
a:hover,
a:focus {
background: var(--color-purple);
color: white;
}
.mention img {
--size: 1rem;
display: block;
width: var(--size);
height: var(--size);
border-radius: 50%;
overflow: hidden;
margin-right: 0.25em;
}
</style>

View file

@ -0,0 +1,40 @@
---
import { Sprite } from 'astro-icon';
import { social } from '../config.ts';
const { invert = false, marginBottom = false } = Astro.props;
const items = [
{ href: '/blog', title: 'Blog' },
{ href: 'https://docs.astro.build', title: 'Docs' },
{ href: '/play', title: 'Playground', hiddenMobile: true }
]
---
<nav id="nav" class={`main ${invert ? 'invert' : ''} ${marginBottom ? 'margin-bottom' : ''}`.trim()} style={`--offset: 2.5;`}>
<ul class="container" role="list">
<li class="logo">
<a href="/">
<h1 class="visually-hidden">Astro</h1>
<Sprite class="logomark" name="logo" size="3em" />
<Sprite class="wordmark" name="wordmark" height="3.5em" width="6em" />
</a>
</li>
{items.map(item => (
<li class={`${item.hiddenMobile ? 'hidden-mobile' : ''}`}>
<a aria-current={Astro.request.url.pathname.startsWith(item.href) ? 'page' : undefined} href={item.href}>
{item.title}
</a>
</li>
))}
{social.map(item => (
<li class="icon">
<a href={item.href} title={item.title}>
<Sprite name={item.icon} size="1.5em" role="presentation" />
</a>
</li>
))}
</ul>
</nav>

View file

@ -0,0 +1,58 @@
---
export interface Props {
title?: string;
type?: 'tip' | 'warning' | 'error';
}
const { type = 'tip', title } = Astro.props;
---
<aside class={`note type-${type}`}>
{title && <h3 class="head-sm">{title}</h3>}
<slot />
</aside>
<style>
.note {
--padding-block: 2rem;
--padding-inline: 2rem;
display: flex;
flex-direction: column;
padding: var(--padding-block) var(--padding-inline);
margin-left: calc(var(--padding-inline) * -1);
margin-right: calc(var(--padding-inline) * -1);
margin-top: 4rem !important;
background: var(--color-dawn);
color: var(--color-midnight);
box-shadow: var(--shadow-md);
}
@media (min-width: 64rem) {
.note {
border-radius: var(--corner-md);
}
}
.note > :global(* + *) {
margin-top: 1em;
}
.note h3 {
font-weight: 700;
font-size: var(--size-500);
color: var(--color-accent);
margin: 0;
}
.note.type-tip {
--color-accent: var(--color-blue);
}
.note.type-warning {
--color-accent: var(--color-yellow);
}
.note.type-error {
--color-accent: var(--color-red);
}
</style>

View file

@ -0,0 +1,59 @@
---
const { class: className, background = 'color-tan', offset = 0, size = "lg", pad = 1 } = Astro.props;
let style = `--pad: ${pad}; `;
if (offset !== 0) {
style += `--offset-block: ${offset};`
}
---
<div class="container" style={style}>
<article class={`panel elevation-xl size-${size} ${className}`} style={`--background: ${background};`}>
{Astro.slots.title && <div class="title">
<slot name="title" />
</div>}
<slot />
</article>
</div>
<style>
.container {
position: relative;
z-index: 1;
margin-top: calc(var(--offset-block, 0) * -1rem);
}
.panel {
--offset-inline: 0.5rem;
background: var(--background);
border-radius: var(--corner-md);
padding: calc(1.25rem * var(--pad, 1)) 1.5rem;
display: flex;
flex-direction: column;
align-items: center;
margin-left: calc(var(--offset-inline) * -1);
margin-right: calc(var(--offset-inline) * -1);
}
.panel.size-md {
max-width: 64rem;
margin-left: auto;
margin-right: auto;
}
@media (min-width: 45rem) {
.panel {
padding: calc(3.25rem * var(--pad, 1)) 2rem;
--offset-inline: 1.625rem;
}
}
.title {
display: flex;
flex-direction: column;
align-items: center;
text-align: center;
color: var(--color-dusk);
}
.panel > :not(p):nth-child(2) {
margin-top: 3rem;
}
</style>

View file

@ -0,0 +1,102 @@
---
const { class: className = '', href } = Astro.props;
// Wrap in <span> because Houdini is disabled for a[href] for security
const { variant = 'primary' } = Astro.props;
const { before, after } = Astro.slots;
---
<span class={`link pixel variant-${variant} ${before ? 'has-before' : ''} ${after ? 'has-after' : ''} ${className}`.trim()}>
<a {href}>
<slot name="before" />
<span><slot /></span>
<slot name="after" />
</a>
</span>
<style>
.link {
--border-radius: 8;
--duration: 200ms;
--delay: 30ms;
--background: linear-gradient(180deg, var(--link-color-stop-a), var(--link-color-stop-b));
display: flex;
color: white;
font-family: var(--font-display);
font-size: 1.5rem;
width: max-content;
transition-property: transform, --link-color-stop-a, --link-color-stop-b;
transition-duration: var(--duration);
transition-delay: var(--delay);
transition-timing-function: cubic-bezier(0.22, 1, 0.36, 1);
}
.link:hover,
.link:focus-within {
transform: translateY(calc(var(--pixel-size) * -0.5px));
}
.link:active {
transform: translateY(0);
}
.has-before a :first-child {
margin-left: -1rem;
margin-right: 0.25rem;
}
.has-before a :last-child {
margin-left: 0.25rem;
margin-right: -1rem;
}
a {
display: flex;
align-items: center;
justify-content: center;
padding: 0.67rem 2.5rem;
width: 100%;
height: 100%;
text-decoration: none;
color: inherit !important;
}
a > :global(* + *) {
margin-left: 0.25rem;
}
.variant-primary {
--variant: primary;
--background: linear-gradient(180deg, var(--link-color-stop-a), var(--link-color-stop-b));
}
.variant-primary:hover,
.variant-primary:focus-within {
--link-color-stop-a: #6D39FF;
--link-color-stop-b: #AF43FF;
}
.variant-primary:active {
--link-color-stop-a: #5F31E1;
--link-color-stop-b: #A740F3;
}
.variant-outline {
--variant: outline;
--background: none;
color: var(--background);
}
.variant-outline > a::before {
position: absolute;
top: 0;
right: calc(var(--pixel-size) * 1px);
bottom: calc(var(--pixel-size) * 1px);
left: calc(var(--pixel-size) * 1px);
content: "";
display: block;
transform-origin: bottom center;
background: linear-gradient(to top, var(--background), rgba(255, 255, 255, 0));
opacity: 0.3;
transform: scaleY(0);
transition: transform 200ms cubic-bezier(0.22, 1, 0.36, 1);
}
.variant-outline:hover > a::before,
.variant-outline:focus-within > a::before {
transform: scaleY(1);
}
.variant-outline:active > a::before {
transform: scaleY(1);
}
</style>

View file

@ -0,0 +1,19 @@
{Astro.request.url.pathname === '/' && <noscript></noscript>}
<script type="module" hoist>
import "../scripts/nav.ts";
</script>
<script type="module" src="/scripts/defer.js"></script>
<script type="module">
const checkbox = document.querySelector('#marquee-pause');
if(checkbox) {
function onChange(element) {
const paused = element.checked;
localStorage.setItem('astro:marquee-paused', `${paused}`);
}
checkbox.addEventListener('change', ({ target }) => onChange(target))
checkbox.addEventListener('keydown', ({ target, key }) => {
if (key !== 'Enter') return;
onChange(target);
});
}
</script>

View file

@ -0,0 +1,28 @@
---
export interface Props {
code: string;
}
const { code } = Astro.props;
---
<pre><code>{String(code).trim().split('\n').map(
line => <span class="line">{
line.startsWith('#') ? <span class="comment">{line}</span> : line
}</span>)
}</code></pre>
<style>
pre,
code {
white-space: pre;
}
.comment {
color: var(--color-gray-400);
}
.line {
display: block;
}
.line:empty::after {
content: ' ';
}
</style>

View file

@ -0,0 +1 @@
<a href="#content" class="skip-link">Skip to main content</a>

View file

@ -0,0 +1,33 @@
---
const { height = 50 } = Astro.props;
---
<div class="starfield" style={`--height: ${height}%;`} />
<style>
.starfield {
position: absolute;
top: 0;
right: 0;
bottom: calc(100% - var(--height));
left: 0;
background-image: url(/stars.png);
background-repeat: repeat;
-webkit-mask-image: url(/stars-mask.png);
-webkit-mask-repeat: repeat;
mask-image: url(/stars-mask.png);
mask-repeat: repeat;
animation: sparkle 10s linear infinite alternate-reverse;
pointer-events: none;
}
@keyframes sparkle {
from {
-webkit-mask-position: top right;
mask-position: top right;
}
to {
-webkit-mask-position: bottom left;
mask-position: bottom left;
}
}
</style>

View file

@ -0,0 +1,14 @@
---
import Panel from './Panel.astro'
---
<Panel class="tweet" background="var(--color-dawn)">
<slot />
</Panel>
<style>
.tweet {
margin-top: 2rem;
display: block !important;
}
</style>

View file

@ -0,0 +1,9 @@
<div class="wrapper">
<slot />
</div>
<style>
.wrapper {
background: var(--color-tan) linear-gradient(to bottom, var(--color-tan) 0%, var(--color-tan) calc(100% - 50vh), #E8ADB7 100%);
}
</style>

View file

@ -0,0 +1,106 @@
---
import DateTime from '../../components/Date.astro';
import Author from '../../components/Author.astro';
import { smartypants } from 'smartypants';
const { post = {}, variant = 'summary' } = Astro.props;
const Title = variant === 'summary' ? 'h2' : 'h1';
---
<div class="wrapper">
<Title class="title text-gradient" set:html={smartypants(post.title, 1)} />
<div class="authors">
<h3>Written by</h3>
<ul role="list">
{post.authors.map(author => (
<li><Author name={author} /></li>
))}
</ul>
</div>
<div class="date">
<h3>Published on</h3>
<p><DateTime value={post.publishDate} /></p>
</div>
</div>
<style>
.wrapper {
width: 100%;
max-width: 48rem;
margin: 0 auto;
display: flex;
flex-flow: row wrap;
--space: 0.5rem;
}
@media (min-width: 40rem) {
.wrapper {
--space: 1rem;
}
}
.title {
font-family: var(--font-display);
margin: 0 auto;
flex-grow: 1;
width: 100%;
line-height: 1.1;
}
h1.title {
font-size: var(--size-800);
}
h2.title {
--fill: var(--color-dusk);
font-size: var(--size-600);
}
.description {
flex-grow: 1;
width: 100%;
margin-top: var(--space);
}
.description > p {
max-width: 48ch;
}
.post > div {
margin-top: calc(var(--space) * 2);
}
.authors {
flex-grow: 2;
width: 100%;
}
@media (min-width: 52rem) {
.authors {
width: initial;
}
}
.authors ul {
display: flex;
flex-flow: column nowrap;
list-style: none;
}
.authors ul > li + li {
margin-top: 0.5rem;
}
.date {
flex-grow: 1;
}
h3 {
margin: 0;
margin-bottom: var(--size-300);
font-family: var(--font-display);
font-size: var(--size-300);
font-weight: 700;
color: var(--color-dusk);
text-transform: uppercase;
letter-spacing: 1px;
}
.date, .authors {
margin-top: calc(var(--space) * 2);
font-size: var(--size-400);
}
:is(.date, .authors) p {
margin: 0;
font-size: inherit;
height: 2rem;
}
</style>

View file

@ -0,0 +1,53 @@
---
import { Sprite } from 'astro-icon';
const { tweet: { title, href } } = Astro.props;
---
<section class="outro">
<div class="container">
<div class="content">
<a class="return" href="/blog">
<Sprite pack="mdi" name="arrow-left" size="32" aria-hidden="true" />
<span>Return to Blog</span>
</a>
<a class="share-on-twitter" title="Share on Twitter" href={`https://twitter.com/intent/tweet?text=${encodeURIComponent(title)}&url=${encodeURIComponent(href)}&via=astrodotbuild`}>
<Sprite pack="mdi" name="twitter" size="16" />
<span>Share</span>
</a>
</div>
</div>
</section>
<style>
.outro {
padding: 8rem 0 2rem;
width: 100%;
}
.content {
display: flex;
align-items: center;
justify-content: space-between;
max-width: 48rem;
padding: 0;
margin: 0 auto;
}
.return {
display: flex;
align-items: center;
justify-content: center;
font-size: var(--size-600);
font-family: var(--font-display);
color: var(--color-purple);
gap: 0.25em;
}
.share-on-twitter {
background: var(--color-blue);
color: white;
display: flex;
align-items: center;
justify-content: center;
padding: 0.25em 0.5em;
gap: 0.25em;
}
</style>

View file

@ -0,0 +1,88 @@
---
import { smartypants } from 'smartypants';
const { career = {}, variant = 'summary' } = Astro.props;
const Title = variant === 'summary' ? 'h2' : 'h1';
---
<div class="wrapper">
<Title class="title text-gradient">{smartypants(career.title, 1)}</Title>
<div class="description">
<p>{career.description}</p>
</div>
</div>
<style>
.wrapper {
width: 100%;
max-width: 48rem;
margin: 0 auto;
display: flex;
flex-flow: row wrap;
}
.title {
font-family: var(--font-display);
margin: 0 auto;
flex-grow: 1;
width: 100%;
line-height: 1.1;
}
h1.title {
font-size: var(--size-800);
}
h2.title {
--fill: var(--color-dusk);
font-size: var(--size-600);
}
.description {
flex-grow: 1;
width: 100%;
margin-top: 0.5em;
}
.description > p {
max-width: 48ch;
color: #999;
}
.post > div {
margin-top: 2em;
}
.authors {
flex-grow: 2;
width: 100%;
}
@media (min-width: 52rem) {
.authors {
width: initial;
}
}
.authors ul {
display: flex;
flex-flow: column nowrap;
list-style: none;
}
.authors ul > li + li {
margin-top: 0.5rem;
}
.date {
flex-grow: 1;
}
h3 {
margin: 0;
margin-bottom: var(--size-300);
font-family: var(--font-display);
font-size: var(--size-300);
font-weight: 700;
color: var(--color-dusk);
text-transform: uppercase;
letter-spacing: 1px;
}
.date, .authors {
margin-top: 2rem;
font-size: var(--size-400);
}
:is(.date, .authors) p {
margin: 0;
font-size: inherit;
height: 2rem;
}
</style>

View file

@ -0,0 +1,34 @@
---
import Section from './Section.astro';
import Title from './Title.astro';
import Panel from '../Panel.astro';
---
<Section {...Astro.props}>
<Panel background="var(--gradient-pop-4)">
<div class="container">
<div class="content">
<h3 class="head-md"><slot name="title" /></h3>
<div class="body">
<slot />
</div>
</div>
</div>
</Panel>
</Section>
<style>
.content {
color: var(--color-dawn);
text-align: center;
}
.body {
max-width: 75ch;
margin: 2em auto 0;
}
.body :global(a) {
text-decoration: underline;
color: white;
}
</style>

View file

@ -0,0 +1,31 @@
---
import Panel from '../Panel.astro';
import Checklist from '../Checklist.astro';
---
<section>
<Panel background="var(--gradient-pop-3)">
<Fragment slot="title">
<h3 class="head-md">Batteries Included.</h3>
<p>Astro does the heavy lifting in the background&mdash;no plugins required.</p>
</Fragment>
<Checklist>
<li>Markdown Support</li>
<li>TypeScript</li>
<li>CSS Modules</li>
<li>PostCSS, Sass, Less, Stylus</li>
<li>Built-in SEO</li>
<li>Sitemaps</li>
<li>RSS Feeds</li>
<li>Pagination</li>
</Checklist>
</Panel>
</section>
<style>
section {
background: var(--color-tan);
padding-bottom: 20rem;
}
</style>

View file

@ -0,0 +1,71 @@
---
import Section from './Section.astro';
import Title from './Title.astro';
import ArrowLink from '../ArrowLink.astro';
import Panel from '../Panel.astro';
import { social } from '../../config.ts';
const { style } = Astro.props;
---
<Section class="community" style={style} pad={1.0}>
<Panel size="md" background="var(--color-dawn)">
<Fragment slot="title">
<h3 class="head-md"><Title id="community"><slot name="title">Connect with Astro</slot></Title></h3>
<div class="description">
<slot>
<p>Learn more about Astro, get support, and meet thousands of other devs in our Discord community!</p>
</slot>
</div>
</Fragment>
<ul class="links">
<li class="social">
<ArrowLink icon="mdi:book" href="https://docs.astro.build">
Documentation
</ArrowLink>
</li>
{social.map(item => (
<li class="social">
<ArrowLink icon={item.icon} href={item.href}>
{item.title}
</ArrowLink>
</li>
))}
</ul>
</Panel>
</Section>
<style>
.community {
padding-top: var(--size-1000);
background: var(--background, rgba(255, 255, 255, 0));
}
.description {
margin-top: 1rem;
max-width: 48ch;
}
.links {
margin: 0;
padding: 0;
width: 100%;
list-style: none;
display: flex;
flex-flow: row wrap;
align-items: center;
justify-content: space-evenly;
gap: 0.5rem;
}
.links > :global(li) {
display: flex;
align-items: center;
justify-content: center;
text-align: center;
width: 100%;
}
@media (min-width: 52rem) {
.links > :global(li) {
width: auto;
}
}
</style>

View file

@ -0,0 +1,68 @@
---
import Section from './Section.astro';
const { reverse, gradient = false } = Astro.props;
---
<Section pad={2} class={"demo" + (gradient ? ' gradient' : '')}>
<div class="container">
<div class="content">
<h3 class="head-md title"><slot name="title" /></h3>
<slot />
</div>
<div class={['illo', reverse ? 'reverse' : ''].filter(x => x).join(' ')}>
<slot name="illustration" />
</div>
</div>
</Section>
<style>
.demo:nth-child(3) {
padding-top: 4rem;
}
.reverse {
padding-bottom: var(--size-1000);
}
.gradient {
background: var(--color-tan) linear-gradient(180deg, #E5DAEE 0%, rgba(255, 255, 255, 0) 100%);
}
.container {
display: grid;
gap: 4rem;
}
.content > :global(p) {
margin-top: 0.5rem;
margin-bottom: 0.5rem;
max-width: 52ch;
}
.content {
display: flex;
flex-direction: column;
align-items: flex-start;
justify-content: center;
}
.illo {
display: flex;
align-items: center;
justify-content: center;
}
@media (min-width: 64rem) {
.reverse {
order: -1;
}
.title {
margin-top: -2.5ex;
}
.container {
grid-template-columns: repeat(2, minmax(0, 1fr));
}
.content {
padding: 2rem;
}
}
@media (min-width: 82rem) {
.illo :global(svg) {
transform: scale(1);
}
}
</style>

View file

@ -0,0 +1,166 @@
---
import { Sprite } from 'astro-icon';
const { before, after, name } = Astro.props;
---
<div class="illustration" role="img" data-name={name}>
<div class="before-astro">
<div class="screen">
<slot name="before" />
</div>
<div class="label">
<div class="badge">
<Sprite pack="ic" name="close" size={24} />
</div>
<div class="text">
<h4>{before}</h4>
<h5>Before Astro</h5>
</div>
</div>
</div>
<div class="after-astro">
<div class="screen">
<slot name="after" />
</div>
<div class="label">
<div class="badge">
<Sprite name="logo-single-color" size={20} />
</div>
<div class="text">
<h4>{after}</h4>
<h5>With Astro</h5>
</div>
</div>
</div>
</div>
<style>
.illustration {
display: grid;
grid-template-columns: repeat(2, minmax(0, 1fr));
gap: 2rem;
z-index: 1;
}
.screen {
position: relative;
height: 248px;
width: 165px;
border-radius: var(--corner-md);
box-shadow: var(--shadow-xl);
display: flex;
flex-direction: column;
overflow: hidden;
}
@media (min-width: 40rem) {
.screen {
height: 330px;
width: 220px;
}
}
.before-astro {
--color: #E3CFC7;
--color-rgb: 227, 207, 199;
}
.after-astro {
--color: #E8D6F3;
--color-rgb: 232, 214, 243;
}
.before-astro :is(.label, .screen) {
background: #FAFAFA linear-gradient(to bottom, rgba(255, 255, 255, 0) 0%, #F4EFED 100%);
}
.after-astro :is(.label, .screen) {
background: #FAFAFA linear-gradient(to bottom, rgba(255, 255, 255, 0) 0%, #F3E9FA 100%);
}
.after-astro .screen::before {
content: "";
position: absolute;
top: 0;
right: 0;
bottom: 0;
left: 0;
background: var(--gradient-pop-5);
filter: blur(10px);
z-index: -1;
}
.before-astro {
z-index: 0;
}
.after-astro {
z-index: 1;
}
.after-astro .screen {
margin-left: -24px;
margin-top: 64px;
}
@media (min-width: 40rem) {
.after-astro .screen {
margin-top: initial;
margin-left: initial;
}
}
.label {
position: relative;
width: 165px;
margin: -1.5rem 0 0;
display: flex;
align-items: center;
padding: 0.5rem 1rem;
z-index: 2;
border-radius: 2rem;
box-shadow: var(--shadow-md);
}
.after-astro .label {
margin-left: -1.5rem;
}
@media (min-width: 40rem) {
.label {
margin: -1.5rem auto 0;
width: 175px;
}
.after-astro .label {
margin-left: auto;
}
}
.text {
margin-left: 0.5rem;
}
.text h4,
.text h5 {
font-family: var(--font-display);
margin: 0;
line-height: 1;
}
.text h4 {
font-size: var(--size-400);
color: var(--color-midnight);
}
.text h5 {
font-size: var(--size-300);
color: #7C678B;
margin-top: 0.25em;
}
.before-astro .text h5 {
color: #CC2727;
}
.after-astro .text h5 {
color: #4a54ee;
}
.badge {
--size: 2rem;
width: var(--size);
height: var(--size);
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
color: white;
z-index: 2;
}
.before-astro .badge {
background: linear-gradient(180deg, #FA8C5D 0%, #CC2727 100%);
}
.after-astro .badge {
background: linear-gradient(180.2deg, #205EFF 0.17%, #C238BD 114.76%);
}
</style>

View file

@ -0,0 +1,177 @@
---
import Illustration from './DemoIllustration.astro';
---
<Illustration before="255 kB" after="10.7 kB" name="hydration">
<Fragment slot="before">
<div class="ph-content before">
<header>
<div class="logo" style="--i: 0" />
<div class="cart dynamic" style="--i: 0.5" />
</header>
<ul role="list">
{[0, 1, 2].map((i) => {
return (
<li>
<div class="img" style={`--i: ${(i * 0.5) + 1}`}>
<svg xmlns="http://www.w3.org/2000/svg" fill="none"width="48" height="44" viewBox="0 0 64 59" role="img">
<path fill="#FAFAFA" d="M13 1h5v1h2v1h2v2h1v3h3v2h1v5h-1v2h-3v2h-1v3h-2v1h-2v1h-5v-1h-2v-1H8v-3H7v-2H5v-2H4v-5h1V8h2V5h1V3h3V2h2V1ZM1 54h3v-4h3v-3h3v-3h3v-3h2v-3h5v3h4v3h2v3h5v-3h2v-4h3v-4h1v-5h2v-3h2v-3h2v-3h2v3h2v3h2v3h2v5h2v4h2v4h2v3h3v4h1v4h2v4H1v-5Z"/>
</svg>
</div>
<span class="group">
<div style={`--i: ${(i * 0.5) + 1.33}`} />
<div class="dynamic" style={`--i: ${(i * 0.5) + 1.67}`} />
</span>
</li>
)
})}
</ul>
</div>
</Fragment>
<Fragment slot="after">
<div class="ph-content after">
<header>
<div class="logo" />
<div class="cart dynamic" style="--i: 0" />
</header>
<ul role="list">
{[0, 1, 2].map((i) => {
return (
<li>
<div class="img">
<svg xmlns="http://www.w3.org/2000/svg" fill="none" width="48" height="44" viewBox="0 0 64 59" role="img">
<path fill="#FAFAFA" d="M13 1h5v1h2v1h2v2h1v3h3v2h1v5h-1v2h-3v2h-1v3h-2v1h-2v1h-5v-1h-2v-1H8v-3H7v-2H5v-2H4v-5h1V8h2V5h1V3h3V2h2V1ZM1 54h3v-4h3v-3h3v-3h3v-3h2v-3h5v3h4v3h2v3h5v-3h2v-4h3v-4h1v-5h2v-3h2v-3h2v-3h2v3h2v3h2v3h2v5h2v4h2v4h2v3h3v4h1v4h2v4H1v-5Z"/>
</svg>
</div>
<span class="group">
<div />
<div class="dynamic" style={`--i: ${i * 0.5}`} />
</span>
</li>
)
})}
</ul>
</div>
</Fragment>
</Illustration>
<style>
.before {
--accent: 94, 81, 76;
--accent-bg: linear-gradient(to bottom, #DFC9C0 0%, #A8938A 100%);
--delay: 100ms;
}
.after {
--accent: 175, 67, 255;
--accent-bg: var(--gradient-pop-3);
--delay: 200ms;
}
.ph-content {
color: var(--color);
position: absolute;
top: 0;
right: 0;
bottom: 0;
left: 0;
width: 100%;
height: 100%;
display: flex;
flex-direction: column;
padding: 1rem;
}
.ph-content > * + * {
margin-top: 1rem;
}
ul {
list-style: none;
display: grid;
grid-template-rows: repeat(3, 1fr);
gap: 1rem;
}
li {
width: 100%;
display: flex;
align-items: flex-start;
justify-content: space-between;
flex-flow: row nowrap;
}
.group {
margin-left: 1rem;
flex-grow: 1;
display: flex;
flex-direction: column;
}
.group div {
width: 100%;
flex-grow: 1;
height: 1.5rem;
}
.group div:last-child {
margin-top: 0.5rem;
width: 67%;
}
header {
--size: 2rem;
display: flex;
justify-content: space-between;
width: 100%;
height: var(--size);
}
:where(.ph-content div) {
border-radius: 8px;
background: var(--color);
width: 100%;
height: var(--height, var(--size, 1rem));
position: relative;
z-index: 2;
}
.img {
position: relative;
border-radius: 8px;
z-index: 2;
height: 64px;
width: 64px;
padding: 0.5rem;
}
.logo {
width: calc(var(--size) * 3);
height: var(--size);
}
.cart {
width: var(--size);
height: var(--size);
border-radius: 50%;
}
.ph-content .dynamic {
background: var(--accent-bg);
}
.ph-content div[style*="--i"]::after {
content: "";
position: absolute;
display: block;
top: 0;
right: 0;
bottom: 0;
left: 0;
z-index: -1;
border-radius: inherit;
opacity: 1;
}
.ph-content div[style*="--i"]::after {
box-shadow: 0 0 0 2px rgba(var(--accent), 1);
opacity: 0;
animation: hydrate-border 3s calc(var(--delay) + calc(var(--i) * 250ms)) ease-out forwards;
}
@keyframes hydrate-border {
0% {
opacity: 0;
}
50%, 80% {
opacity: 1;
}
100% {
opacity: 0;
}
}
</style>

View file

@ -0,0 +1,206 @@
---
import Illustration from './DemoIllustration.astro';
---
<Illustration before="4.0s TTI" after="2.3s TTI" name="tti">
<Fragment slot="before">
<div class="spinner-container before">
<svg class="spinner" viewBox="0 0 100 100" width="128" height="128" fill="none" role="status">
<circle cx="50" cy="50" r="25" stroke="currentColor" stroke-width="8" />
</svg>
</div>
<div class="tti-content before">
<svg class="img" xmlns="http://www.w3.org/2000/svg" fill="none" width="188" height="148" role="img" viewBox="0 0 198 156">
<path fill="url(#tti-before-a)" fill-rule="evenodd" d="M7 0C3 0 0 3 0 7v141c0 4 3 7 7 7h183c4 0 7-3 7-7V7c0-4-3-7-7-7H7Zm65 19h-9v2h-5v2h-5v5h-2v5h-5v5h-2v9h2v5h5v5h2v4h5v3h5v2h9v-2h5v-3h5v-4h2v-5h5v-5h2v-9h-2v-5h-5v-5h-2v-5h-5v-2h-5v-2ZM45 128h-7v9h128v-8h-4v-8h-3v-7h-5v-7h-4v-9h-4v-7h-5V81h-4v-7h-4v-5h-3v-7h-6v7h-4v5h-3v7h-4v10h-3v7h-5v9h-6v7h-9v-7h-5v-6h-7v-7H67v7h-5v6h-5v7h-6v6h-6v8Z" clip-rule="evenodd"/>
<defs>
<linearGradient id="tti-before-a" x1="98.7" x2="98.7" y1="0" y2="155.2" gradientUnits="userSpaceOnUse">
<stop stop-color="#DFC9C0"/>
<stop offset="1" stop-color="#A8938A"/>
</linearGradient>
</defs>
</svg>
<div />
<div />
<div />
</div>
</Fragment>
<Fragment slot="after">
<div class="spinner-container after">
<svg class="spinner" viewBox="0 0 100 100" width="128" height="128" fill="none" role="status">
<circle cx="50" cy="50" r="25" stroke="currentColor" stroke-width="8" />
</svg>
</div>
<div class="tti-content after">
<svg class="img" xmlns="http://www.w3.org/2000/svg" fill="none" width="188" height="148" viewBox="0 0 198 156" role="img">
<path fill="url(#tti-after-a)" fill-rule="evenodd" d="M8 1C4 1 0 4 0 8v140c0 5 4 8 8 8h182c5 0 8-3 8-8V8c0-4-3-7-8-7H8Zm65 18H63v3h-5v2h-4v5h-3v5h-4v4h-3v10h3v5h4v4h3v5h4v2h5v3h10v-3h4v-2h5v-5h3v-4h4v-5h3V38h-3v-4h-4v-5h-3v-5h-5v-2h-4v-3ZM45 129h-6v9h128v-8h-4v-8h-4v-8h-4v-6h-5v-9h-4v-8h-4V81h-5v-6h-3v-6h-4v-6h-5v6h-4v6h-4v6h-3v10h-4v8h-5v9h-5v6h-9v-6h-6v-7h-7v-6H67v6h-5v7h-5v6h-6v7h-6v8Z" clip-rule="evenodd"/>
<defs>
<linearGradient id="tti-after-a" x1="70.4" x2="70.4" y1=".6" y2="155.8" gradientUnits="userSpaceOnUse">
<stop stop-color="#FFB4B4"/>
<stop offset="1" stop-color="#C487F0"/>
</linearGradient>
</defs>
</svg>
<div />
<div />
<div />
</div>
</Fragment>
</Illustration>
<style>
.spinner {
--size: 8rem;
--len: 156px;
--rot: 360deg;
transform-origin: center center;
display: flex;
align-items: center;
justify-content: center;
width: var(--size);
height: var(--size);
margin: auto;
color: var(--color);
stroke-dasharray: var(--len);
stroke-dashoffset: calc(var(--len) * 0.25);
animation: spin 1.5s cubic-bezier(0.445, 0.05, 0.55, 0.95) infinite;
}
@media (min-width: 40rem) {
.spinner {
--size: 12rem;
}
}
.spinner > circle {
transform-origin: center center;
animation: rotate 6s linear infinite;
}
.spinner-container {
position: absolute;
top: 0;
right: 0;
bottom: 0;
left: 0;
width: 100%;
height: 100%;
display: flex;
flex-direction: column;
}
.before,
.after {
animation: fade-out 10s linear infinite both;
}
.spinner-container.before {
animation-name: fade-out-before;
}
.tti-content.before {
animation-name: fade-in-before;
}
.spinner-container.after {
animation-name: fade-out-after;
}
.tti-content.after {
animation-name: fade-in-after;
}
.tti-content {
opacity: 0;
position: absolute;
top: 0;
right: 0;
bottom: 0;
left: 0;
width: 100%;
height: 100%;
display: flex;
flex-direction: column;
padding: 1rem;
}
.img {
width: 100%;
}
.tti-content > * + * {
margin-top: 1rem;
}
.tti-content > div {
height: var(--height, 1rem);
width: var(--width, 100%);
border-radius: 8px;
background: var(--color);
}
.tti-content > :nth-child(2) {
--height: 1.5rem;
--width: 67%;
}
.tti-content > :nth-child(4) {
--width: 80%;
}
@media (min-width: 40rem) {
.tti-content > :nth-child(2) {
--height: 2rem;
--width: 67%;
}
}
@keyframes rotate {
from {
transform: rotate(calc(var(--rot) * 0));
}
to {
transform: rotate(calc(var(--rot) * 1));
}
}
@keyframes spin {
0% {
transform: rotate(calc(var(--rot) * 0));
stroke-dashoffset: calc(var(--len) * 0.25);
}
25% {
transform: rotate(calc(var(--rot) * 0.25));
stroke-dashoffset: calc(var(--len) * 0.5);
}
50% {
transform: rotate(calc(var(--rot) * 0.5));
stroke-dashoffset: calc(var(--len) * 0.25);
}
75% {
transform: rotate(calc(var(--rot) * 0.75));
stroke-dashoffset: calc(var(--len) * 0.5);
}
100% {
transform: rotate(calc(var(--rot) * 1));
stroke-dashoffset: calc(var(--len) * 0.25);
}
}
@keyframes fade-in-before {
0%, 39.9%, 100% {
opacity: 0;
}
40%, 85% {
opacity: 1;
}
}
@keyframes fade-out-before {
0%, 39.9%, 100% {
opacity: 1;
}
40%, 99.9% {
opacity: 0;
}
}
@keyframes fade-in-after {
0%, 22.9%, 100% {
opacity: 0;
}
23%, 85% {
opacity: 1;
}
}
@keyframes fade-out-after {
0%, 22.9%, 100% {
opacity: 1;
}
23%, 99.9% {
opacity: 0;
}
}
</style>

View file

@ -0,0 +1,82 @@
---
const { class: className } = Astro.props;
---
<div class={`grid ${className}`}>
<div class="base" />
{Array.from({ length: 10 }, (_, i) => <div class="line" style={`--i: ${i};`} />)}
</div>
<style>
.grid {
--len: 10;
--time: 15s;
--grid-size-inline: 5em;
--grid-size-block: 2.5em;
--grid-color: #000;
--grid-stroke: 1px;
opacity: 0.7;
mix-blend-mode: hard-light;
position: absolute;
bottom: -30%;
left: 0;
right: 0;
height: 100%;
z-index: 1;
filter: invert(1);
pointer-events: none;
}
.base {
position: absolute;
top: 0;
right: 0;
bottom: 0;
left: 0;
margin-left: -50%;
width: 200%;
background-position-y: 0px;
background-image: repeating-linear-gradient(90deg, var(--grid-color, white) 0%, transparent calc(1px + var(--grid-stroke, 0px)), transparent var(--grid-size-inline), var(--grid-color, white) calc(var(--grid-size-inline) + 1px + var(--grid-stroke, 0px)));
transform: perspective(50vh) rotateX(60deg) translateZ(10px) translateY(-1px);
-webkit-mask-image: linear-gradient(to bottom, rgba(0, 0, 0, 0), rgba(0, 0, 0, 1), rgba(0, 0, 0, 1));
mask-image: linear-gradient(to bottom, rgba(0, 0, 0, 0), rgba(0, 0, 0, 1), rgba(0, 0, 0, 1))
}
.line {
--n-start: calc((var(--i) + 1) / var(--len));
--n-end: calc((var(--i) + 2) / var(--len));
--ty-start: calc(var(--i) * calc(var(--grid-size-block) * ((var(--i) + 1) / var(--len))));
--ty-end: calc((var(--i) + 1) * calc(var(--grid-size-block) * ((var(--i) + 2) / var(--len))));
position: absolute;
right: 0;
left: 0;
top: 35.5%;
height: 1px;
width: 100%;
border: var(--grid-stroke) solid var(--grid-color);
transform: scaleY(var(--sy-start)) translateY(var(--ty-start));
animation: move var(--time) infinite linear;
}
/* Firefox tweaks */
@supports (-moz-appearance: none) {
.line {
animation-play-state: paused;
}
}
@media (prefers-reduced-motion: reduce) {
.line {
animation-play-state: paused;
}
}
@keyframes move {
0% {
opacity: calc(var(--n-start));
transform: translate3d(0, var(--ty-start), 0);
}
100%{
opacity: calc(var(--n-end));
transform: translate3d(0, var(--ty-end), 0);
}
}
</style>

View file

@ -0,0 +1,206 @@
---
import Grid from './Grid.astro';
---
<section class="hero">
<div class="background">
<div class="blob-a" />
<div class="blob-b" />
<div class="blob-c" />
</div>
<Grid />
<div class="container content">
<h2 class="text-gradient"><slot name="title" /></h2>
<p><slot name="tagline" /></p>
<div class="cta container">
<slot />
</div>
</div>
</section>
<style>
.hero {
position: relative;
background: var(--color-tan) linear-gradient(180deg, var(--color-tan), var(--color-tan) 10rem, #E8ADB7 100%);
padding: var(--padding, 4rem) 0;
padding-bottom: var(--padding-bottom, 16rem);
z-index: 0;
overflow: hidden;
max-height: 95vh;
max-height: 95svh;
}
@supports (padding: clamp(0px, 1vw, 1px)) {
.hero {
--padding: clamp(4rem, 6vw, 8rem);
--padding-bottom: max(calc(var(--padding) * 1.5), 24rem);
}
}
@media (min-width: 52rem) and (min-height: 40rem) {
.hero {
max-height: 80vh;
}
}
.content {
margin-top: 5rem;
text-align: center;
color: rgba(34, 34, 34, 0.8);
}
@media (min-width: 52rem) {
.content {
margin-top: 3.5rem;
}
}
h2 {
--fill: var(--gradient-pop-1);
font-size: 4rem;
font-size: var(--size-800);
font-family: var(--font-display);
line-height: 1.1;
margin-bottom: 1.25rem;
}
@media (min-width: 52rem) {
h2 {
font-size: var(--size-900);
}
}
p {
font-size: var(--size-600);
max-width: min(48ch, calc(100% - 1rem));
margin-left: auto;
margin-right: auto;
margin-bottom: 1.5em;
}
.cta {
position: relative;
left: 0;
right: 0;
display: flex;
flex-flow: column nowrap;
align-items: center;
justify-content: center;
z-index: 1;
gap: 2rem;
}
@media (min-width: 52rem) {
.cta {
position: absolute;
flex-direction: row;
}
}
.background {
--time: 25s;
--blur: 128px;
--scale-x: 1.125;
--opacity: 0.9;
--ty: 0;
--aspect-ratio: 16 / 9;
position: absolute;
top: 0;
right: 0;
bottom: 0;
left: 0;
overflow: hidden;
pointer-events: none;
z-index: -1;
backface-visibility: hidden;
transform: translate3d(0,0,0) scaleX(var(--scale-x, 1));
opacity: var(--opacity, 1);
filter: blur(var(--blur)) saturate(1.1);
}
@media (min-width: 64rem) {
.background {
--ty: -5%;
/* --opacity: 1; */
--scale-x: 1.25;
}
}
/* Firefox tweaks */
@supports (-moz-appearance: none) {
.background {
--blur: 1280px;
--opacity: 0.7;
}
}
/* Safari tweaks */
@supports (-webkit-appearance: none) {
.background {
--blur: 80px;
--opacity: 0.8;
}
}
.blob-a {
display: block;
position: absolute;
top: 67%;
right: -50%;
margin-left: -50%;
min-width: 1280px;
width: 200%;
aspect-ratio: var(--aspect-ratio);
border-radius: 50%;
transform-origin: center center;
background: linear-gradient(-170deg, #FF9900 0%, #AD00FF 30.71%, #0029FF 37.67%, #6D39FF 49.54%);
z-index: 0;
}
.blob-b {
content: '';
display: block;
position: absolute;
top: 67%;
right: -50%;
margin-left: -50%;
min-width: 1280px;
width: 200%;
aspect-ratio: var(--aspect-ratio);
border-radius: 50%;
background: red;
transform-origin: center center;
background: linear-gradient(259deg, #FF9900 0%, #AD00FF 30.71%, #0029FF 37.67%, #6D39FF 49.54%);
z-index: -1;
}
.blob-c {
content: '';
display: block;
position: absolute;
top: 50%;
left: -50%;
margin-left: -50%;
min-width: 1280px;
width: 200%;
aspect-ratio: var(--aspect-ratio);
border-radius: 50%;
background: red;
transform-origin: center center;
background: linear-gradient(-180deg, #FF9900 0%, #AD00FF 30.71%, #0029FF 37.67%, #6D39FF 49.54%);
z-index: 1;
}
@supports not (aspect-ratio: 1 / 1) {
.blob-a::before,
.blob-b::before,
.blob-c::before {
float: left;
padding-top: calc(100% * var(--aspect-ratio));
content: "";
}
.blob-a::after,
.blob-b::after,
.blob-c::after {
display: block;
content: "";
clear: both;
}
}
</style>

View file

@ -0,0 +1,89 @@
---
import Section from './Section.astro';
import { Icon } from 'astro-icon';
const rows = [
['tailwindcss', 'sass', 'postcss', 'markdown', 'javascript', 'typescript'],
['rss', 'wordpress', 'strapi', 'prismic', 'shopify'],
['contentful', 'netlify-grid', 'vercel-grid', 'cloudflare', 'github'],
]
---
<Section id="frameworks">
<div class="container content">
<h3 class="head-md title"><slot name="title" /></h3>
<div class="body">
<slot />
</div>
</div>
<ul class="integrations">
{rows.map(row => row.map(icon => <li class={`icon-${icon}`}><Icon title={icon.replace(/\-grid$/, '')} name={`logos/${icon}`} height="64" /></li>))}
</ul>
</Section>
<style>
#frameworks {
padding-top: var(--size-1000);
padding-bottom: var(--size-1000);
}
.content {
display: grid;
color: var(--color-dusk);
}
.title {
margin-bottom: 1rem;
}
.body > :global(* + *) {
margin-top: 1rem;
}
ul {
list-style: none;
}
.integrations {
box-sizing: border-box;
--cols: 2;
margin: 2rem -1rem;
display: flex;
flex-flow: row wrap;
justify-content: space-evenly;
padding: 4rem 0;
opacity: 0.4;
color: black;
}
.integrations > li {
border: 1rem solid transparent;
display: flex;
justify-content: center;
align-items: center;
}
.integrations svg {
height: var(--size-800);
}
@media (min-width: 42rem) {
.integrations {
margin: 2rem auto;
padding-left: 2rem;
padding-right: 2rem;
}
}
@media (min-width: 64rem) {
.integrations {
max-width: 86rem;
}
.integrations svg {
height: var(--size-900);
}
.content {
grid-template-columns: repeat(4, minmax(0, 1fr));
gap: 1rem;
}
.title {
grid-column: 1 / 3;
}
.body {
grid-column: 3 / 5;
}
}
</style>

View file

@ -0,0 +1,148 @@
---
import Section from './Section.astro';
import Panel from '../Panel.astro';
import Starfield from '../Starfield.astro';
const tweet = {
content: 'Rebuilt my Next.js blog using Astro out of curiosity&hellip; holy shit the difference in bundle size.',
href: 'https://twitter.com/t3dotgg/status/1437195415439360003',
}
---
<Section class="performance" style="--background: var(--gradient-pop-4);">
<Starfield height={48} />
<div class="container">
<div class="content">
<h3 class="head-md"><slot name="title" /></h3>
<div class="body">
<slot />
</div>
</div>
<aside class="data">
<Panel class="data" background="linear-gradient(to bottom, #F0DBDB, #E5BAE2)">
<p class="quote head-sm" style="font-weight: 700;" set:html={tweet.content}></p>
<div class="bars">
<h4 id="payload-title">Total Payload</h4>
<div class="chart" style="--max: 138;">
<div class="bar" style="--value: 138; --background: #C699BB;" aria-labelledby="payload-title" aria-describedby="payload-before" role="progressbar" aria-valuenow="138" aria-valuemin="0" aria-valuemax="138" />
<h5 id="payload-before" class="before">Before: 138 <abbr title="kilobytes">kB</abbr></h5>
<div class="bar" style="--value: 7.5; --background: var(--gradient-pop-2);" aria-labelledby="payload-title" aria-describedby="payload-after" role="progressbar" aria-valuenow="7.5" aria-valuemin="0" aria-valuemax="138" />
<h5 id="payload-after" class="after">Using Astro: 7.5 <abbr title="kilobytes">kB</abbr></h5>
</div>
<!-- <h4 id="tti-title">Time to Interactive (TTI)</h4>
<div class="chart" style="--max: 4;">
<div class="bar" style="--value: 4; --background: #C699BB;" aria-labelledby="tti-title" aria-describedby="tti-before" role="progressbar" aria-valuenow="4" aria-valuemin="0" aria-valuemax="4" />
<h5 id="tti-before" class="before">Before: 4<abbr title="seconds">s</abbr></h5>
<div class="bar" style="--value: 2.3; --background: var(--gradient-pop-2);" aria-labelledby="tti-title" aria-describedby="tti-after" role="progressbar" aria-valuenow="2.3" aria-valuemin="0" aria-valuemax="4" />
<h5 id="tti-after" class="after">Using Astro: 2.3<abbr title="seconds">s</abbr></h5>
</div> -->
<p>Real world data from <a href={tweet.href}>@t3dotgg on Twitter</a></p>
</div>
</Panel>
</aside>
</div>
</Section>
<style>
.performance {
position: relative;
padding-top: var(--size-1000);
padding-left: 1rem;
padding-right: 1rem;
}
.content {
color: white;
}
.container {
display: grid;
gap: 4rem;
padding: 0;
align-items: center;
}
h3 {
margin-bottom: 1rem;
}
abbr {
text-decoration: none;
color: inherit;
}
.data .quote {
display: flex;
flex-direction: column;
}
.data .quote::before {
font: inherit;
font-size: 2em;
content: open-quote;
margin-top: -0.5ex;
margin-bottom: -1ex;
}
.bars {
display: flex;
flex-direction: column;
width: 100%;
margin-top: 2rem !important;
}
#payload-label {
margin-top: 0.25rem;
display: flex;
width: 100%;
align-items: center;
justify-content: space-between;
}
.chart {
margin-top: 0.5rem;
position: relative;
display: flex;
flex-flow: column nowrap;
}
.bar {
position: relative;
width: 100%;
height: 1.25rem;
border-radius: 4px;
overflow: hidden;
}
.bar::after {
content: '';
position: absolute;
border-radius: inherit;
height: 100%;
top: 0;
bottom: 0;
left: 0;
width: calc(calc(var(--value) / var(--max)) * 100%);
background: var(--background);
}
.bars h4 {
margin-top: 1rem;
font-size: var(--size-400);
font-family: var(--font-display);
}
.bars h5, .bars p {
font-weight: 400;
font-size: var(--size-300);
font-family: var(--font-display);
}
.bars h5 {
padding: 0.25rem 0 0.5rem;
}
.bars p {
margin-top: 2rem !important;
margin-bottom: -1rem;
}
@media (min-width: 52rem) {
.data {
order: initial;
}
.container {
grid-template-columns: repeat(2, minmax(0, 1fr));
}
}
</style>

View file

@ -0,0 +1,83 @@
---
import Section from './Section.astro';
const { pad = 1, background, blobs = true, color } = Astro.props;
---
<Section class="quote" background={background} pad={pad} style={color ? `--color: ${color};` : ''}>
{blobs && <div class="glow" />}
<div class="logo">
<slot name="logo" />
</div>
<blockquote class="container">
<p class="head-md"><slot name="quote" /></p>
<cite class="head-md"><slot name="cite" /></cite>
</blockquote>
</Section>
<style>
.quote {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
position: relative;
color: var(--color, var(--color-dusk));
padding-top: calc(var(--size-1000) * var(--pad, 1));
padding-bottom: calc(var(--size-1000) * var(--pad, 1));
z-index: 0;
}
blockquote {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
text-align: center;
font-weight: bold;
padding: 4rem 2rem;
font-size: inherit;
margin: 0 auto;
max-width: 85ch;
}
cite {
margin-top: 2rem;
font-style: normal;
font-size: var(--size-600);
}
.logo > :global(a) {
padding: 1rem;
display: inline-flex;
color: inherit;
}
.glow {
pointer-events: none;
position: absolute;
--size: 256px;
width: calc(var(--size) * 3);
border-radius: 50%;
opacity: 0.25;
mix-blend-mode: hard-light;
height: var(--size);
background: var(--blob-color, var(--gradient-pop-4));
filter: blur(128px);
z-index: -1;
}
.glow::before,
.glow::after {
content: "";
display: block;
position: absolute;
--size: 12rem;
width: var(--size);
height: var(--size);
border-radius: 50%;
background: var(--blob-color, var(--gradient-pop-4));
}
@media (min-width: 64rem) {
blockquote {
padding: 0 8rem;
}
}
</style>

View file

@ -0,0 +1,19 @@
---
const { class: className = '', style: _style, pad = 1, ...props } = Astro.props;
let style;
if (_style) {
style = `${_style}; --pad: ${pad};`
} else {
style = `--pad: ${pad};`
}
---
<section class={className} style={style} {...props}>
<slot />
</section>
<style>
section {
background: var(--background, var(--color-tan));
padding: 0 0 calc(var(--size-1000) * var(--pad, 1)) 0;
}
</style>

View file

@ -0,0 +1,187 @@
---
// SEE https://developers.google.com/search/docs/advanced/guidelines/qualify-outbound-links for rel="sponsored" info
import { Icon } from 'astro-icon';
import Section from './Section.astro';
const exclusive = [
{ name: 'netlify', title: 'Netlify', href: 'https://netlify.com', width: '240', height: '64' },
]
const gold = [
{ name: 'vercel', title: 'Vercel', href: 'https://vercel.com', width: '180', height: '48' },
{ name: 'divriots', title: 'divRIOTS', href: 'https://divriots.com', width: '192', height: '48' },
{ name: 'stackup', title: 'StackUp Digital', href: 'https://stackupdigital.co.uk/', width: '162', height: '40' }
]
const users = await fetch(`https://opencollective.com/astrodotbuild/members.json`).then(res => res.json());
const skipTier = new Set(['Platinum Sponsor', 'Gold Sponsor']);
function isUser(user) {
return user.role === 'BACKER' && !skipTier.has(user.tier);
}
function getUser(user) {
return {
name: user.name,
image: user.image,
initials: user.name.split(' ').slice(0, 2).map(word => word[0].toUpperCase()).join(''),
href: user.website ?? user.twitter ?? user.github
};
};
const individuals = users.filter(user => isUser(user)).map(user => getUser(user)).sort(() => 0.5 - Math.random());
---
<Section class="sponsor-section" pad={2} {...Astro.props}>
<div class="container">
<h3 class="head-sm title"><slot name="title" /></h3>
<ul class="sponsors exclusive">
{exclusive.map(sponsor => (
<li><a href={sponsor.href} title={sponsor.title}><span><Icon name={`sponsors/${sponsor.name}`} width={sponsor.width} height={sponsor.height} aria-hidden="true" /></span></a></li>
))}
</ul>
<ul class="sponsors gold">
{gold.map(sponsor => (
<li><a href={sponsor.href} title={sponsor.title} rel="sponsored"><span><Icon name={`sponsors/${sponsor.name}`} width={sponsor.width} height={sponsor.height} aria-hidden="true" /></span></a></li>
))}
</ul>
<ul class="sponsors individuals">
{individuals.map(user => {
const Tag = user.href ? 'a' : 'span';
const tagProps = user.href ? { href: user.href, rel: 'sponsored' } : {};
return (
<li>
<Tag class="individual" title={user.name} {...tagProps}>
<span data-initials={user.initials}>
<img aria-hidden="true" loading="lazy" src={user.image} width="64" height="64" alt={user.name} />
</span>
</Tag>
</li>
)
})}
</ul>
<div class="cta"><slot name="cta" /></div>
</div>
</Section>
<style>
.sponsor-section {
padding-top: calc(var(--size-1000) * var(--pad, 1));
--background: linear-gradient(to bottom, var(--color-tan) 0%, #E8ADB7 100%);
}
.container {
}
.title {
text-align: center;
margin-bottom: 4rem;
}
.sponsors a {
display: inline-block;
position: relative;
z-index: 1;
color: black;
}
.sponsors a > span {
display: inline-block;
}
.sponsors a > span > svg {
pointer-events: none;
}
.sponsors + .sponsors {
margin-top: 2.5rem;
}
.cta {
font-family: var(--font-display);
text-align: center;
color: var(--color-dusk);
margin-top: 2rem;
}
ul.individuals {
list-style: none;
display: flex;
flex-flow: row wrap;
align-items: center;
justify-content: center;
gap: 0.5rem;
max-width: 40rem;
}
.individuals > li {
box-sizing: border-box;
border: 0.25rem solid rgba(255, 255, 255, 0);
}
.individual {
position: relative;
--size: 3rem;
width: var(--size);
height: var(--size);
border-radius: 50%;
overflow: hidden;
display: flex;
flex-direction: column;
}
.individual img {
pointer-events: none;
position: relative;
width: var(--size);
height: var(--size);
aspect-ratio: 1 / 1;
background: white;
display: flex;
align-items: center;
justify-content: center;
overflow: hidden;
}
.individual span::before {
content: "";
content: attr(data-initials);
position: absolute;
top: 0;
right: 0;
bottom: 0;
left: 0;
z-index: 0;
background: var(--color-dusk);
color: white;
font-family: var(--font-display);
font-size: 1.25rem;
display: flex;
align-items: center;
justify-content: center;
text-align: center;
}
.individual img:not([src]) {
display: none;
}
.sponsors {
margin: 0 auto;
padding: 0;
max-width: max-content;
list-style: none;
display: flex;
align-items: center;
justify-content: center;
flex-flow: row wrap;
gap: 1.5rem;
color: var(--color-dusk);
}
@media (min-width: 52rem) {
.sponsors {
grid-auto-flow: column;
max-width: max-content;
margin: 0 auto;
gap: 2rem;
grid-template-columns: repeat(auto-fit, minmax(0, 1fr));
}
}
@media (min-width: 64rem) {
.sponsors {
gap: 4rem;
}
}
.sponsors > li {
display: flex;
align-items: center;
justify-content: center;
text-align: center;
}
</style>

View file

@ -0,0 +1,30 @@
---
const { id } = Astro.props;
if (!id) {
throw new Error(`<Title> requires an "id" prop! Recieved ${typeof id}`);
}
---
<a href={`#${id}`} id={id}>
<slot />
</a>
<style>
a {
font: inherit;
color: inherit;
text-decoration: none;
}
a::after {
content: "#";
color: inherit;
opacity: 0;
transition: opacity 200ms cubic-bezier(0.23, 1, 0.320, 1);
}
a:active::after,
a:hover::after,
a:focus::after {
opacity: 0.6;
}
</style>

View file

@ -0,0 +1,69 @@
---
import Panel from '../Panel.astro'
import Section from './Section.astro'
---
<Section id="trusted">
<Panel background="var(--color-dawn)" offset={4}>
<Fragment slot="title">
<h3 class="head-sm"><slot name="title" /></h3>
</Fragment>
<ul class="logos">
<slot />
</ul>
</Panel>
</Section>
<style>
#trusted {
background: linear-gradient(to bottom, #D8C5EF 0%, #E5DAEE 100%);
padding-top: 0;
}
.logos {
margin: 0;
padding: 0;
width: 100%;
list-style: none;
display: grid;
grid-template-columns: repeat(1, minmax(0, 1fr));
gap: 1.5rem;
color: var(--color-dusk);
}
@media (min-width: 32rem) {
.logos {
grid-template-columns: repeat(2, minmax(0, 1fr));
}
* :global(li:last-child) {
grid-column: auto / span 2;
}
}
@media (min-width: 64rem) {
.logos {
grid-auto-flow: column;
max-width: max-content;
margin: 0 auto;
gap: 3rem;
grid-template-columns: repeat(auto-fit, minmax(0, 1fr));
}
* :global(li:last-child) {
grid-column: auto / auto;
}
.logos :global(svg) {
height: 2.25rem;
}
}
@media (min-width: 82rem) {
.logos :global(svg) {
height: 2.5rem;
}
}
.logos > :global(li) {
display: flex;
align-items: center;
justify-content: center;
text-align: center;
width: 100%;
}
</style>

View file

@ -0,0 +1,17 @@
export const social = [
{
icon: 'mdi:github',
title: 'Github',
href: 'https://github.com/withastro/astro'
},
{
icon: 'fa-brands:discord',
title: 'Discord',
href: 'https://astro.build/chat'
},
{
icon: 'mdi:twitter',
title: '@astrodotbuild',
href: 'https://twitter.com/astrodotbuild'
},
]

File diff suppressed because one or more lines are too long

View file

@ -0,0 +1,48 @@
<svg width="73" height="46" viewBox="0 0 73 46" fill="none" xmlns="http://www.w3.org/2000/svg">
<circle cx="29.5" cy="18.5" r="17.5" fill="#C4C4C4"/>
<circle cx="29.5" cy="18.5" r="17.5" fill="url(#paint0_linear_706_219)"/>
<g filter="url(#filter0_dddd_706_219)">
<circle cx="29.5" cy="18.5" r="18.5" fill="url(#paint1_linear_706_219)"/>
</g>
<path fill-rule="evenodd" clip-rule="evenodd" d="M32.6986 10.0057C32.8635 10.2045 32.9476 10.4729 33.1159 11.0094L36.7913 22.733C35.4107 22.0374 33.929 21.5503 32.3971 21.2885L30.0041 13.4362C29.985 13.3736 29.9456 13.3186 29.8916 13.2795C29.8377 13.2405 29.7722 13.2194 29.7049 13.2195C29.6377 13.2196 29.5722 13.2408 29.5184 13.28C29.4646 13.3193 29.4253 13.3743 29.4064 13.437L27.0423 21.2845C25.5036 21.5451 24.015 22.033 22.6284 22.7313L26.3218 11.0068C26.4907 10.471 26.575 10.2032 26.7399 10.0047C26.8856 9.82949 27.075 9.69337 27.2902 9.60938C27.5339 9.51428 27.8224 9.51428 28.3994 9.51428H31.0375C31.6152 9.51428 31.904 9.51428 32.148 9.60962C32.3634 9.69378 32.5529 9.83013 32.6986 10.0057Z" fill="white"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M32.8973 23.4193C32.3272 23.9301 31.1892 24.2784 29.8784 24.2784C28.2695 24.2784 26.921 23.7536 26.5633 23.048C26.4353 23.4525 26.4067 23.9153 26.4067 24.211C26.4067 24.211 26.3223 25.6629 27.2863 26.6728C27.2863 26.1484 27.692 25.7233 28.1926 25.7233C29.0505 25.7233 29.0495 26.5075 29.0487 27.1436V27.2004C29.0487 28.166 29.6121 28.9937 30.4132 29.3426C30.2899 29.0767 30.226 28.7849 30.2264 28.4893C30.2264 27.5683 30.7425 27.2254 31.3423 26.827C31.8196 26.5099 32.3498 26.1576 32.7152 25.4508C32.912 25.0701 33.0148 24.6439 33.0141 24.211C33.0141 23.935 32.9732 23.669 32.8973 23.4193Z" fill="white"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M32.8973 23.4193C32.3272 23.9301 31.1892 24.2784 29.8784 24.2784C28.2695 24.2784 26.921 23.7536 26.5633 23.048C26.4353 23.4525 26.4067 23.9153 26.4067 24.211C26.4067 24.211 26.3223 25.6629 27.2863 26.6728C27.2863 26.1484 27.692 25.7233 28.1926 25.7233C29.0505 25.7233 29.0495 26.5075 29.0487 27.1436V27.2004C29.0487 28.166 29.6121 28.9937 30.4132 29.3426C30.2899 29.0767 30.226 28.7849 30.2264 28.4893C30.2264 27.5683 30.7425 27.2254 31.3423 26.827C31.8196 26.5099 32.3498 26.1576 32.7152 25.4508C32.912 25.0701 33.0148 24.6439 33.0141 24.211C33.0141 23.935 32.9732 23.669 32.8973 23.4193Z" fill="url(#paint2_linear_706_219)"/>
<defs>
<filter id="filter0_dddd_706_219" x="-13.8429" y="0" width="86.6857" height="88.8" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB">
<feFlood flood-opacity="0" result="BackgroundImageFix"/>
<feColorMatrix in="SourceAlpha" type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0" result="hardAlpha"/>
<feOffset dy="1.22083"/>
<feGaussianBlur stdDeviation="0.562542"/>
<feColorMatrix type="matrix" values="0 0 0 0 0.192157 0 0 0 0 0.0745098 0 0 0 0 0.227451 0 0 0 0.0418093 0"/>
<feBlend mode="normal" in2="BackgroundImageFix" result="effect1_dropShadow_706_219"/>
<feColorMatrix in="SourceAlpha" type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0" result="hardAlpha"/>
<feOffset dy="3.37545"/>
<feGaussianBlur stdDeviation="1.55536"/>
<feColorMatrix type="matrix" values="0 0 0 0 0.192157 0 0 0 0 0.0745098 0 0 0 0 0.227451 0 0 0 0.06 0"/>
<feBlend mode="normal" in2="effect1_dropShadow_706_219" result="effect2_dropShadow_706_219"/>
<feColorMatrix in="SourceAlpha" type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0" result="hardAlpha"/>
<feOffset dy="8.12679"/>
<feGaussianBlur stdDeviation="3.7447"/>
<feColorMatrix type="matrix" values="0 0 0 0 0.192157 0 0 0 0 0.0745098 0 0 0 0 0.227451 0 0 0 0.0781907 0"/>
<feBlend mode="normal" in2="effect2_dropShadow_706_219" result="effect3_dropShadow_706_219"/>
<feColorMatrix in="SourceAlpha" type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0" result="hardAlpha"/>
<feOffset dy="26.9571"/>
<feGaussianBlur stdDeviation="12.4214"/>
<feColorMatrix type="matrix" values="0 0 0 0 0.192157 0 0 0 0 0.0745098 0 0 0 0 0.227451 0 0 0 0.12 0"/>
<feBlend mode="normal" in2="effect3_dropShadow_706_219" result="effect4_dropShadow_706_219"/>
<feBlend mode="normal" in="SourceGraphic" in2="effect4_dropShadow_706_219" result="shape"/>
</filter>
<linearGradient id="paint0_linear_706_219" x1="29.5" y1="1" x2="29.5" y2="36" gradientUnits="userSpaceOnUse">
<stop stop-color="#FA8C5D"/>
<stop offset="1" stop-color="#CC2727"/>
</linearGradient>
<linearGradient id="paint1_linear_706_219" x1="29.5" y1="-4.36615e-09" x2="29.3535" y2="42.5428" gradientUnits="userSpaceOnUse">
<stop stop-color="#205EFF"/>
<stop offset="1" stop-color="#C238BD"/>
</linearGradient>
<linearGradient id="paint2_linear_706_219" x1="29.7095" y1="23.048" x2="29.7095" y2="29.3426" gradientUnits="userSpaceOnUse">
<stop stop-color="#FFCD83"/>
<stop offset="1" stop-color="#EC672C"/>
</linearGradient>
</defs>
</svg>

After

Width:  |  Height:  |  Size: 4.8 KiB

View file

@ -0,0 +1,37 @@
<svg width="72" height="45" viewBox="0 0 72 45" fill="none" xmlns="http://www.w3.org/2000/svg">
<g filter="url(#filter0_dddd_706_218)">
<circle cx="29.5" cy="17.5" r="17.5" fill="#C4C4C4"/>
<circle cx="29.5" cy="17.5" r="17.5" fill="url(#paint0_linear_706_218)"/>
</g>
<path fill-rule="evenodd" clip-rule="evenodd" d="M29.75 19.4606L35.7893 25.4999L37.4999 23.7893L31.4606 17.75L37.5 11.7107L35.7894 10.0001L29.75 16.0394L23.7106 10L22 11.7106L28.0394 17.75L22 23.7894L23.7106 25.5L29.75 19.4606Z" fill="white"/>
<defs>
<filter id="filter0_dddd_706_218" x="-12.8429" y="0" width="84.6857" height="86.8" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB">
<feFlood flood-opacity="0" result="BackgroundImageFix"/>
<feColorMatrix in="SourceAlpha" type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0" result="hardAlpha"/>
<feOffset dy="1.22083"/>
<feGaussianBlur stdDeviation="0.562542"/>
<feColorMatrix type="matrix" values="0 0 0 0 0.192157 0 0 0 0 0.0745098 0 0 0 0 0.227451 0 0 0 0.0418093 0"/>
<feBlend mode="normal" in2="BackgroundImageFix" result="effect1_dropShadow_706_218"/>
<feColorMatrix in="SourceAlpha" type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0" result="hardAlpha"/>
<feOffset dy="3.37545"/>
<feGaussianBlur stdDeviation="1.55536"/>
<feColorMatrix type="matrix" values="0 0 0 0 0.192157 0 0 0 0 0.0745098 0 0 0 0 0.227451 0 0 0 0.06 0"/>
<feBlend mode="normal" in2="effect1_dropShadow_706_218" result="effect2_dropShadow_706_218"/>
<feColorMatrix in="SourceAlpha" type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0" result="hardAlpha"/>
<feOffset dy="8.12679"/>
<feGaussianBlur stdDeviation="3.7447"/>
<feColorMatrix type="matrix" values="0 0 0 0 0.192157 0 0 0 0 0.0745098 0 0 0 0 0.227451 0 0 0 0.0781907 0"/>
<feBlend mode="normal" in2="effect2_dropShadow_706_218" result="effect3_dropShadow_706_218"/>
<feColorMatrix in="SourceAlpha" type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0" result="hardAlpha"/>
<feOffset dy="26.9571"/>
<feGaussianBlur stdDeviation="12.4214"/>
<feColorMatrix type="matrix" values="0 0 0 0 0.192157 0 0 0 0 0.0745098 0 0 0 0 0.227451 0 0 0 0.12 0"/>
<feBlend mode="normal" in2="effect3_dropShadow_706_218" result="effect4_dropShadow_706_218"/>
<feBlend mode="normal" in="SourceGraphic" in2="effect4_dropShadow_706_218" result="shape"/>
</filter>
<linearGradient id="paint0_linear_706_218" x1="29.5" y1="0" x2="29.5" y2="35" gradientUnits="userSpaceOnUse">
<stop stop-color="#FA8C5D"/>
<stop offset="1" stop-color="#CC2727"/>
</linearGradient>
</defs>
</svg>

After

Width:  |  Height:  |  Size: 2.5 KiB

View file

@ -0,0 +1,4 @@
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 190 190">
<path fill="currentColor" fill-rule="evenodd" d="m148 52 42 42-42 42-42-42 42-42Z" clip-rule="evenodd" opacity=".4"/>
<path fill="currentColor" fill-rule="evenodd" d="m42 52 88 87H45L0 94l42-42Z" clip-rule="evenodd"/>
</svg>

After

Width:  |  Height:  |  Size: 304 B

View file

@ -0,0 +1,3 @@
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 190 190">
<path fill="currentColor" fill-rule="evenodd" d="M25 20h140c3 0 5 2 5 5v140c0 3-2 5-5 5H25c-3 0-5-2-5-5V25c0-3 2-5 5-5Zm111 126c-7 0-11-4-14-9l-12 7c4 8 13 14 26 14s23-7 23-19-7-17-19-23l-3-1c-6-3-9-4-9-9 0-3 3-6 7-6s7 2 9 6l11-7c-5-8-11-11-20-11-13 0-21 8-21 19s7 17 17 21l4 1c6 3 10 5 10 10 0 4-4 7-9 7Zm-55 0c-5 0-7-4-9-8l-12 7c4 7 10 13 22 13s21-6 21-21V89H88v48c0 7-2 9-7 9Z" clip-rule="evenodd"/>
</svg>

After

Width:  |  Height:  |  Size: 487 B

View file

@ -0,0 +1,4 @@
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 190 190">
<path fill="currentColor" d="M95 143V79l33-33v65l-33 32Zm-65 0 32 33v-65H46"/>
<path fill="currentColor" d="M62 111V46l33-32v65l-33 32Zm66 65v-65l32-32v64l-32 33Zm-98-33V79l32 32"/>
</svg>

After

Width:  |  Height:  |  Size: 268 B

View file

@ -0,0 +1,6 @@
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 190 190">
<path fill="currentColor" d="m101 61 24-10c6-2 12-3 17-2 4 0 7 1 9 4 1 2 2 5 1 9l-6 16-10 13a181 181 0 0 0-35-30ZM54 91l-9-13c-4-6-6-11-7-16 0-4 0-7 2-9 2-3 4-4 9-4 4-1 10 0 17 2l24 10a190 190 0 0 0-36 30Z"/>
<path fill="currentColor" fill-rule="evenodd" d="M58 95a174 174 0 0 1 37-30 182 182 0 0 1 37 30 174 174 0 0 1-37 30 182 182 0 0 1-37-30Zm37 13a13 13 0 1 0 0-26 13 13 0 0 0 0 26Z" clip-rule="evenodd"/>
<path fill="currentColor" d="m54 99-9 13c-4 6-6 11-7 16 0 4 0 7 2 9 2 3 4 4 9 4 4 1 10 0 17-2l24-10a191 191 0 0 1-36-30Zm47 30 24 10c6 2 12 3 17 2 4 0 7-1 9-4 1-2 2-5 1-9l-6-16-10-13a181 181 0 0 1-35 30Z"/>
<path fill="currentColor" fill-rule="evenodd" d="M178 48 95 0 13 48v95l82 47 83-47V48ZM95 58c10-6 20-10 28-13 7-2 14-3 19-2 6 0 10 2 13 6s4 9 3 14c-1 6-4 12-7 18l-11 14 11 14c3 6 6 12 7 18 1 5 0 10-3 14s-7 6-13 6c-5 1-12 0-19-2-8-3-18-7-28-13-10 6-19 10-28 13-7 2-13 3-19 2-5 0-10-2-13-6s-3-9-2-14c1-6 3-12 7-18l10-14-10-14c-4-6-6-12-7-18-1-5-1-10 2-14s8-6 13-6c6-1 12 0 19 2 9 3 18 7 28 13Z" clip-rule="evenodd"/>
</svg>

After

Width:  |  Height:  |  Size: 1.1 KiB

View file

@ -0,0 +1,4 @@
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 190 190">
<path fill="currentColor" d="M190 95c0-13-16-25-40-32 6-25 3-45-8-51l-8-2v9l4 1c5 3 8 14 6 29l-2 11-25-4c-5-7-10-14-16-19 13-12 25-18 33-18v-9c-11 0-25 8-39 21-14-13-28-21-39-21v9c8 0 20 6 33 18L73 56l-25 4-2-11c-2-15 1-26 6-29l4-1v-9l-9 2c-10 7-13 26-7 51C16 70 0 82 0 95c0 12 16 24 40 32-6 24-3 44 8 50 2 2 5 2 8 2 11 0 25-7 39-21 14 14 28 21 39 21l9-2c10-6 13-26 7-51 24-7 40-19 40-31Zm-50-26-6 15a184 184 0 0 0-10-18l16 3Zm-18 41-9 15a202 202 0 0 1-35 0 213 213 0 0 1-18-30 202 202 0 0 1 17-31 202 202 0 0 1 35 0 213 213 0 0 1 18 31l-8 15Zm12-5 6 15-16 4a212 212 0 0 0 10-19Zm-39 41-11-12a240 240 0 0 0 22 0l-11 12Zm-29-22-16-4 6-15a183 183 0 0 0 10 19Zm29-81 11 12a240 240 0 0 0-22 0l11-12ZM66 66a214 214 0 0 0-10 18l-6-15 16-3Zm-35 48c-14-6-22-13-22-19s8-14 22-20l11-4 9 24-9 23-11-4Zm21 56c-5-3-8-15-6-30l2-11 25 4c5 7 10 14 16 19-13 12-25 19-33 19l-4-1Zm92-30c2 15-1 26-6 29l-4 1c-8 0-20-6-33-18 6-5 11-12 16-19l25-4 2 11Zm15-26-11 4-9-23 9-24 11 4c14 6 22 14 22 20s-9 13-22 19Z"/>
<path fill="currentColor" d="M95 112a18 18 0 1 0 0-35 18 18 0 0 0 0 35Z"/>
</svg>

After

Width:  |  Height:  |  Size: 1.1 KiB

View file

@ -0,0 +1,6 @@
<svg width="190" height="190" viewBox="0 0 190 190" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M127.61 135.052C121.624 127.573 113.542 122.052 104.399 119.195C95.2561 116.338 85.4683 116.275 76.2893 119.014L10 140.398C10 140.398 66.6667 183.165 110.503 172.473L113.711 171.404C131.887 166.058 138.302 148.951 127.61 135.052Z" fill="currentColor" fill-opacity="0.85"/>
<path d="M125.783 81.7735C134.926 84.6307 143.008 90.152 148.994 97.6304C154.34 106.184 155.409 114.737 151.132 122.222L129.748 160.712L129.314 160.639C134.288 153.175 134.123 143.518 127.61 135.052C121.624 127.573 113.542 122.052 104.399 119.195C95.2561 116.338 85.4683 116.275 76.2893 119.014L10 140.398L31.3836 102.976L97.673 81.5926C106.852 78.8533 116.64 78.9163 125.783 81.7735Z" fill="currentColor" fill-opacity="0.65"/>
<path d="M57.0441 50.5863L61.3208 49.5171C104.088 39.8945 160.755 81.5926 160.755 81.5926L139.663 88.7366C135.466 85.6958 130.783 83.3362 125.783 81.7735C116.64 78.9163 106.852 78.8533 97.673 81.5926L51.96 96.3387C48.5484 93.9348 45.5491 91.1335 43.1447 88.0077C33.5221 73.0392 38.868 55.9322 57.0441 50.5863Z" fill="currentColor" fill-opacity="0.45"/>
<path d="M79.4969 17.4417C123.333 7.81904 180 49.5172 180 49.5172L160.755 81.5926C160.755 81.5926 104.088 39.8945 61.3208 49.5172L57.0441 50.5863C51.0678 52.344 46.4787 55.3732 43.3525 59.1798C43.2832 59.1664 43.2139 59.1531 43.1447 59.1398L59.1824 31.3411L61.3208 28.1335C64.5283 23.8568 69.8742 20.6492 76.2893 18.5109L79.4969 17.4417Z" fill="currentColor" fill-opacity="0.3"/>
</svg>

After

Width:  |  Height:  |  Size: 1.5 KiB

View file

@ -0,0 +1,3 @@
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 190 190">
<path fill="currentColor" fill-rule="evenodd" d="M87 14c23-15 56-8 72 15v1a50 50 0 0 1 9 38c-1 6-4 12-7 17a50 50 0 0 1-3 51c-4 5-8 10-14 13l-41 26a54 54 0 0 1-81-33v-20c1-7 3-13 7-18a50 50 0 0 1 2-50c4-6 9-10 14-14l42-26ZM63 161c6 2 13 3 20 1l8-4 41-26a28 28 0 0 0 13-31l-5-11a33 33 0 0 0-35-13l-9 4-15 10a9 9 0 0 1-9 0 10 10 0 0 1-6-6v-4a9 9 0 0 1 4-6l41-26 3-1a10 10 0 0 1 10 4l2 6v1l1 1c6 2 12 4 16 8l3 1v-2l1-4a30 30 0 0 0-5-23 33 33 0 0 0-35-13l-8 4-42 26a28 28 0 0 0-13 19 30 30 0 0 0 6 23 33 33 0 0 0 35 13c3 0 5-2 8-3l16-10 2-2a10 10 0 0 1 12 8l1 3a9 9 0 0 1-4 6l-42 26-2 1a10 10 0 0 1-12-10v-1l-2-1c-6-1-11-4-16-8l-2-1-1 2-1 4a30 30 0 0 0 5 23c4 5 10 10 16 12Z" clip-rule="evenodd"/>
</svg>

After

Width:  |  Height:  |  Size: 777 B

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