mirror of
https://github.com/Snigdha-OS/Snigdha-OS.github.io.git
synced 2025-09-05 20:26:43 +02:00
🛠️ build(astro): nuxtjs to astro
This commit is contained in:
9986
.astro/icon.d.ts
vendored
Normal file
9986
.astro/icon.d.ts
vendored
Normal file
File diff suppressed because it is too large
Load Diff
48
.eslintrc.js
Normal file
48
.eslintrc.js
Normal file
@@ -0,0 +1,48 @@
|
||||
module.exports = {
|
||||
env: {
|
||||
node: true,
|
||||
es2022: true,
|
||||
browser: true,
|
||||
},
|
||||
extends: ['eslint:recommended', 'plugin:astro/recommended', 'plugin:astro/jsx-a11y-strict'],
|
||||
parserOptions: {
|
||||
ecmaVersion: 'latest',
|
||||
sourceType: 'module',
|
||||
},
|
||||
rules: {},
|
||||
overrides: [
|
||||
{
|
||||
files: ['*.js'],
|
||||
rules: {
|
||||
'no-mixed-spaces-and-tabs': ['error', 'smart-tabs'],
|
||||
},
|
||||
},
|
||||
{
|
||||
files: ['*.astro'],
|
||||
parser: 'astro-eslint-parser',
|
||||
parserOptions: {
|
||||
parser: '@typescript-eslint/parser',
|
||||
extraFileExtensions: ['.astro'],
|
||||
},
|
||||
rules: {
|
||||
'no-mixed-spaces-and-tabs': ['error', 'smart-tabs'],
|
||||
},
|
||||
},
|
||||
{
|
||||
files: ['*.ts'],
|
||||
parser: '@typescript-eslint/parser',
|
||||
extends: ['plugin:@typescript-eslint/recommended'],
|
||||
rules: {
|
||||
'@typescript-eslint/no-unused-vars': [
|
||||
'error',
|
||||
{ argsIgnorePattern: '^_', destructuredArrayIgnorePattern: '^_' },
|
||||
],
|
||||
'@typescript-eslint/no-non-null-assertion': 'off',
|
||||
},
|
||||
},
|
||||
{
|
||||
files: ['**/*.astro/*.js', '*.astro/*.js'],
|
||||
parser: '@typescript-eslint/parser',
|
||||
},
|
||||
],
|
||||
}
|
1
.github/funding.yml
vendored
Normal file
1
.github/funding.yml
vendored
Normal file
@@ -0,0 +1 @@
|
||||
custom: https://www.buymeacoffee.com/markteekman
|
20
.github/workflows/releases.yml
vendored
Normal file
20
.github/workflows/releases.yml
vendored
Normal file
@@ -0,0 +1,20 @@
|
||||
---
|
||||
name: "prepare-release"
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- "main"
|
||||
|
||||
jobs:
|
||||
pre-release:
|
||||
name: "Prepare Release"
|
||||
runs-on: "ubuntu-latest"
|
||||
|
||||
steps:
|
||||
- uses: "marvinpinto/action-automatic-releases@latest"
|
||||
with:
|
||||
repo_token: "${{ secrets.GITHUB_TOKEN }}"
|
||||
automatic_release_tag: "latest"
|
||||
prerelease: true
|
||||
title: "Next Release"
|
27
.gitignore
vendored
Normal file
27
.gitignore
vendored
Normal file
@@ -0,0 +1,27 @@
|
||||
# build output
|
||||
dist
|
||||
|
||||
# dependencies
|
||||
node_modules/
|
||||
.snowpack/
|
||||
|
||||
# logs
|
||||
npm-debug.log*
|
||||
yarn-debug.log*
|
||||
yarn-error.log*
|
||||
|
||||
# environment variables
|
||||
.env
|
||||
.env.production
|
||||
|
||||
# macOS-specific files
|
||||
.DS_Store
|
||||
|
||||
# vscode settings and dictionaries
|
||||
.vscode
|
||||
|
||||
# webstorm settings and dictionaries
|
||||
.idea
|
||||
|
||||
# package lock
|
||||
package-lock.json
|
2
.npmrc
Normal file
2
.npmrc
Normal file
@@ -0,0 +1,2 @@
|
||||
# Expose Astro dependencies for `pnpm` users
|
||||
shamefully-hoist = true
|
8
.prettierrc
Normal file
8
.prettierrc
Normal file
@@ -0,0 +1,8 @@
|
||||
{
|
||||
"tabWidth": 2,
|
||||
"semi": false,
|
||||
"singleQuote": true,
|
||||
"printWidth": 120,
|
||||
"plugins": ["prettier-plugin-astro", "prettier-plugin-tailwindcss"],
|
||||
"pluginSearchDirs": false
|
||||
}
|
21
LICENSE
Normal file
21
LICENSE
Normal file
@@ -0,0 +1,21 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2023 Mark Teekman
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
65
README.md
Normal file
65
README.md
Normal file
@@ -0,0 +1,65 @@
|
||||
# Accessible Astro Starter
|
||||
|
||||

|
||||
|
||||
Accessible Astro Starter is a ready to use, SEO and a11y friendly blogging theme. It contains plenty of accessible components to build several page types, Tailwind CSS to help you build faster and example pages such as a dynamic Blog, 404, Markdown and MDX. This theme is designed to help you build your project faster and provide a solid base for accessibility!
|
||||
|
||||
🚀 [Live Preview](https://accessible-astro.netlify.app/)
|
||||
|
||||
## ♿ (Accessibility) Features
|
||||
|
||||
- Astro 4.0
|
||||
- Tailwind CSS support
|
||||
- Prettier integration with `prettier-plugin-astro` and `prettier-plugin-tailwind`
|
||||
- ESLint integration with strict accessibility settings for `eslint-plugin-jsx-a11y`
|
||||
- Markdown and MDX support with examples included in the theme
|
||||
- Uses the awesome `astro-icon` package for the icons
|
||||
- Excellent Lighthouse/PageSpeed scores
|
||||
- Accessible landmarks such as `header`, `main`, `footer`, `section` and `nav`
|
||||
- Outline focus indicator which works on dark and light backgrounds
|
||||
- Several `aria` attributes which provide a better experience for screen reader users
|
||||
- `[...page].astro` and `[post].astro` demonstrate the use of dynamic routes and provide a basic blog with breadcrumbs and pagination
|
||||
- `404.astro` provides a custom 404 error page which you can adjust to your needs
|
||||
- `Header.astro` component included in the `DefaultLayout.astro` layout
|
||||
- `Footer.astro` component included in the `DefaultLayout.astro` layout
|
||||
- `SkipLinks.astro` component to skip to either the main menu or the main content
|
||||
- `Navigation.astro` component with keyboard accessible (dropdown) navigation (arrow keys, escape key)
|
||||
- `ResponsiveToggle.astro` component with an accessible responsive toggle button for the mobile navigation
|
||||
- `DarkMode.astro` component toggle with accessible button and a user system preferred color scheme setting
|
||||
- `SiteMeta.astro` SEO component for setting custom meta data on different pages
|
||||
- `.sr-only` utility class for screen reader only text content (hides text visually)
|
||||
- `prefers-reduced-motion` disables animations for users that have this preference turned on
|
||||
- Ships with many components such as Accordions, Breadcrumbs, Modals, Pagination [and many more](https://accessible-astro.dev/accessible-components)
|
||||
- A collection of utility classes such as breakpoints, button classes, font settings, resets and outlines in `src/assets/scss/base`
|
||||
- View Transitions (⚠️ see [astro-docs](https://docs.astro.build/en/guides/view-transitions/#accessibility) for accessibility considerations)
|
||||
|
||||
## 🚀 Getting started
|
||||
|
||||
Clone this theme locally and run any of the following commands in your terminal:
|
||||
|
||||
| Command | Action |
|
||||
| :---------------- | :------------------------------------------- |
|
||||
| `npm install` | Installs dependencies |
|
||||
| `npm run dev` | Starts local dev server at `localhost:4321` |
|
||||
| `npm run build` | Build your production site to `./dist/` |
|
||||
| `npm run preview` | Preview your build locally, before deploying |
|
||||
|
||||
## 📦 Other Accessible Astro projects
|
||||
|
||||
- [Accessible Astro Dashboard](https://github.com/markteekman/accessible-astro-dashboard/)
|
||||
- [Accessible Astro Components](https://github.com/markteekman/accessible-astro-components/)
|
||||
|
||||
## ❤️ Helping out
|
||||
|
||||
If you find that something isn't working right then I'm always happy to hear it to improve this starter! You can contribute in many ways and forms. Let me know by either:
|
||||
|
||||
1. [Filing an issue](https://github.com/markteekman/accessible-astro-starter/issues)
|
||||
2. [Submitting a pull request](https://github.com/markteekman/accessible-astro-starter/pulls)
|
||||
3. [Starting a discussion](https://github.com/markteekman/accessible-astro-starter/discussions)
|
||||
4. [Buying me a coffee!](https://www.buymeacoffee.com/markteekman)
|
||||
|
||||
## ☕ Thank you!
|
||||
|
||||
A big thank you to the creators of the awesome Astro static site generator and to all using this starter to make the web a bit more accessible for all people around the world :)
|
||||
|
||||
[](https://www.buymeacoffee.com/markteekman)
|
13
astro.config.mjs
Normal file
13
astro.config.mjs
Normal file
@@ -0,0 +1,13 @@
|
||||
import { defineConfig } from 'astro/config'
|
||||
import mdx from '@astrojs/mdx'
|
||||
import tailwind from '@astrojs/tailwind'
|
||||
import compress from 'astro-compress'
|
||||
import icon from "astro-icon"
|
||||
|
||||
// https://astro.build/config
|
||||
export default defineConfig({
|
||||
compressHTML: true,
|
||||
integrations: [mdx(), icon(), tailwind({
|
||||
applyBaseStyles: false,
|
||||
}), compress()],
|
||||
})
|
35
package.json
Normal file
35
package.json
Normal file
@@ -0,0 +1,35 @@
|
||||
{
|
||||
"name": "accessible-astro-starter",
|
||||
"description": "An Accessible Starter Theme for Astro including several accessibility features and tools to help you build faster.",
|
||||
"version": "3.1.2",
|
||||
"author": "Mark Teekman",
|
||||
"homepage": "https://accessible-astro.netlify.app/",
|
||||
"scripts": {
|
||||
"dev": "astro dev",
|
||||
"start": "astro dev",
|
||||
"build": "astro build",
|
||||
"preview": "astro preview"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@astrojs/mdx": "^2.0.2",
|
||||
"@astrojs/partytown": "^2.0.2",
|
||||
"@astrojs/tailwind": "^5.0.4",
|
||||
"@iconify-json/ion": "^1.1.15",
|
||||
"@iconify-json/mdi": "^1.1.64",
|
||||
"@typescript-eslint/eslint-plugin": "^5.50.0",
|
||||
"@typescript-eslint/parser": "^5.50.0",
|
||||
"accessible-astro-components": "^2.3.5",
|
||||
"astro": "^4.0.7",
|
||||
"astro-compress": "^2.0.6",
|
||||
"astro-icon": "^1.0.2",
|
||||
"eslint": "^8.33.0",
|
||||
"eslint-plugin-astro": "^0.23.0",
|
||||
"eslint-plugin-jsx-a11y": "^6.7.1",
|
||||
"prettier": "^2.8.3",
|
||||
"prettier-plugin-astro": "^0.8.0",
|
||||
"prettier-plugin-tailwindcss": "^0.2.2",
|
||||
"sass": "^1.49.9",
|
||||
"svgo": "^3.2.0",
|
||||
"tailwindcss": "^3.2.7"
|
||||
}
|
||||
}
|
BIN
public/accessible-components.webp
Normal file
BIN
public/accessible-components.webp
Normal file
Binary file not shown.
After Width: | Height: | Size: 49 KiB |
BIN
public/astronaut-hero-img.webp
Normal file
BIN
public/astronaut-hero-img.webp
Normal file
Binary file not shown.
After Width: | Height: | Size: 331 KiB |
9
public/favicon.svg
Normal file
9
public/favicon.svg
Normal file
@@ -0,0 +1,9 @@
|
||||
<svg width="512" height="512" viewBox="0 0 512 512" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<mask id="mask0_907_31" style="mask-type:alpha" maskUnits="userSpaceOnUse" x="0" y="0" width="512" height="512">
|
||||
<rect width="512" height="512" rx="80" fill="#161618"/>
|
||||
</mask>
|
||||
<g mask="url(#mask0_907_31)">
|
||||
<path d="M166.892 69.3845H390.357L226.065 315.018H2.6001L166.892 69.3845Z" fill="#42E0CE"/>
|
||||
<path d="M176.677 337.783L113.043 432.923H347.108L511.4 187.29H338.645L237.988 337.783H176.677Z" fill="#A14FD6"/>
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 525 B |
BIN
public/fonts/OpenSans-Bold.woff
Normal file
BIN
public/fonts/OpenSans-Bold.woff
Normal file
Binary file not shown.
BIN
public/fonts/OpenSans-Bold.woff2
Normal file
BIN
public/fonts/OpenSans-Bold.woff2
Normal file
Binary file not shown.
BIN
public/fonts/OpenSans-ExtraBold.woff
Normal file
BIN
public/fonts/OpenSans-ExtraBold.woff
Normal file
Binary file not shown.
BIN
public/fonts/OpenSans-ExtraBold.woff2
Normal file
BIN
public/fonts/OpenSans-ExtraBold.woff2
Normal file
Binary file not shown.
BIN
public/fonts/OpenSans-Italic.woff
Normal file
BIN
public/fonts/OpenSans-Italic.woff
Normal file
Binary file not shown.
BIN
public/fonts/OpenSans-Italic.woff2
Normal file
BIN
public/fonts/OpenSans-Italic.woff2
Normal file
Binary file not shown.
BIN
public/fonts/OpenSans-Regular.woff
Normal file
BIN
public/fonts/OpenSans-Regular.woff
Normal file
Binary file not shown.
BIN
public/fonts/OpenSans-Regular.woff2
Normal file
BIN
public/fonts/OpenSans-Regular.woff2
Normal file
Binary file not shown.
BIN
public/social-preview-image.png
Normal file
BIN
public/social-preview-image.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 260 KiB |
BIN
public/wcag-compliant.webp
Normal file
BIN
public/wcag-compliant.webp
Normal file
Binary file not shown.
After Width: | Height: | Size: 59 KiB |
4
src/assets/img/logo.svg
Normal file
4
src/assets/img/logo.svg
Normal file
@@ -0,0 +1,4 @@
|
||||
<svg width="47" height="37" viewBox="0 0 47 37" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M15.5092 1.89746H35.3259L20.7566 23.6923H0.939941L15.5092 1.89746Z" fill="#42E0CE"/>
|
||||
<path d="M16.3771 25.7122L10.7341 34.1539H31.4908L46.0601 12.3591H30.7403L21.8141 25.7122H16.3771Z" fill="#A14FD6"/>
|
||||
</svg>
|
After Width: | Height: | Size: 314 B |
25
src/assets/scss/base/_breakpoint.scss
Normal file
25
src/assets/scss/base/_breakpoint.scss
Normal file
@@ -0,0 +1,25 @@
|
||||
// | -------------------------------------------------------------
|
||||
// | Breakpoint
|
||||
// | -------------------------------------------------------------
|
||||
|
||||
$breakpoints: (
|
||||
"default": 0,
|
||||
"small": 24em,
|
||||
"medium": 48em,
|
||||
"large": 75em
|
||||
) !default;
|
||||
|
||||
@mixin breakpoint($breakpoint) {
|
||||
@if map-has-key($breakpoints, $breakpoint) {
|
||||
@media (min-width: map-get($breakpoints, $breakpoint)) {
|
||||
@content;
|
||||
}
|
||||
} @else if (type_of($breakpoint) == number) {
|
||||
@media (min-width: $breakpoint+"px") {
|
||||
@content;
|
||||
}
|
||||
}
|
||||
@else {
|
||||
@error "Not a correct value, check _base-breakpoints for available values.";
|
||||
}
|
||||
}
|
146
src/assets/scss/base/_button.scss
Normal file
146
src/assets/scss/base/_button.scss
Normal file
@@ -0,0 +1,146 @@
|
||||
// | -------------------------------------------------------------
|
||||
// | Button
|
||||
// | -------------------------------------------------------------
|
||||
|
||||
.button {
|
||||
display: inline-block;
|
||||
padding: 0.75rem 1rem;
|
||||
font-weight: bold;
|
||||
text-decoration: none;
|
||||
text-align: center;
|
||||
color: var(--neutral-900);
|
||||
background-color: var(--primary-100);
|
||||
border: 3px solid var(--primary-100);
|
||||
border-radius: 3px;
|
||||
transition: color 0.15s ease-in-out, background-color 0.15s ease-in-out, border-color 0.15s ease-in-out;
|
||||
|
||||
&:hover,
|
||||
&:focus {
|
||||
text-decoration: underline;
|
||||
background-color: var(--primary-200);
|
||||
border-color: var(--primary-200);
|
||||
}
|
||||
|
||||
&:visited {
|
||||
color: var(--neutral-900);
|
||||
}
|
||||
|
||||
&.color-secondary {
|
||||
background-color: var(--secondary-100);
|
||||
border-color: (var(--secondary-100));
|
||||
|
||||
&:hover,
|
||||
&:focus {
|
||||
background-color: var(--secondary-400);
|
||||
border-color: var(--secondary-400);
|
||||
}
|
||||
}
|
||||
|
||||
&.color-neutral {
|
||||
background-color: var(--neutral-500);
|
||||
border-color: (var(--neutral-500));
|
||||
|
||||
&:hover,
|
||||
&:focus {
|
||||
background-color: var(--neutral-400);
|
||||
border-color: var(--neutral-400);
|
||||
}
|
||||
}
|
||||
|
||||
&.color-info {
|
||||
background-color: var(--info-300);
|
||||
border-color: (var(--info-300));
|
||||
|
||||
&:hover,
|
||||
&:focus {
|
||||
background-color: var(--info-200);
|
||||
border-color: var(--info-200);
|
||||
}
|
||||
}
|
||||
|
||||
&.color-success {
|
||||
background-color: var(--success-400);
|
||||
border-color: (var(--success-400));
|
||||
|
||||
&:hover,
|
||||
&:focus {
|
||||
background-color: var(--success-300);
|
||||
border-color: var(--success-300);
|
||||
}
|
||||
}
|
||||
|
||||
&.color-warning {
|
||||
background-color: var(--warning-400);
|
||||
border-color: (var(--warning-400));
|
||||
|
||||
&:hover,
|
||||
&:focus {
|
||||
background-color: var(--warning-300);
|
||||
border-color: var(--warning-300);
|
||||
}
|
||||
}
|
||||
|
||||
&.color-error {
|
||||
background-color: var(--error-300);
|
||||
border-color: (var(--error-300));
|
||||
|
||||
&:hover,
|
||||
&:focus {
|
||||
background-color: var(--error-200);
|
||||
border-color: var(--error-200);
|
||||
}
|
||||
}
|
||||
|
||||
&.size-tiny {
|
||||
padding: 0.125rem 0.25rem;
|
||||
font-size: 0.75rem;
|
||||
line-height: 1.125rem;
|
||||
}
|
||||
|
||||
&.size-small {
|
||||
padding: 0.25rem 0.5rem;
|
||||
font-size: 0.875rem;
|
||||
line-height: 1.3125rem;
|
||||
}
|
||||
|
||||
&.size-large {
|
||||
padding: 0.75rem 1rem;
|
||||
font-size: 1.125rem;
|
||||
line-height: 1.6875rem;
|
||||
}
|
||||
|
||||
&.size-huge {
|
||||
padding: 1rem 2rem;
|
||||
font-size: 1.25rem;
|
||||
line-height: 1.875rem;
|
||||
}
|
||||
|
||||
&.behavior-full {
|
||||
display: block;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
&.type-secondary {
|
||||
background-color: transparent;
|
||||
|
||||
&:hover,
|
||||
&:focus {
|
||||
background-color: transparent;
|
||||
}
|
||||
}
|
||||
|
||||
&.has-icon {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.5rem;
|
||||
|
||||
[data-icon] {
|
||||
height: auto;
|
||||
width: 30px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.darkmode .button.type-secondary {
|
||||
color: var(--neutral-100);
|
||||
}
|
34
src/assets/scss/base/_color.scss
Normal file
34
src/assets/scss/base/_color.scss
Normal file
@@ -0,0 +1,34 @@
|
||||
// | -------------------------------------------------------------
|
||||
// | Color
|
||||
// | -------------------------------------------------------------
|
||||
|
||||
$colors: (
|
||||
primary: (
|
||||
100: hsl(276, 100%, 79%),
|
||||
200: hsl(276, 79%, 69%),
|
||||
300: hsl(276, 53%, 49%),
|
||||
400: hsl(276, 64%, 48%),
|
||||
500: hsl(276, 96%, 20%),
|
||||
),
|
||||
secondary: (
|
||||
100: hsl(173, 81%, 68%),
|
||||
200: hsl(173, 80%, 63%),
|
||||
300: hsl(173, 72%, 57%),
|
||||
400: hsl(173, 75%, 47%),
|
||||
500: hsl(173, 90%, 30%),
|
||||
),
|
||||
neutral: (
|
||||
100: hsl(0 0% 100%),
|
||||
200: hsl(200 23% 97%),
|
||||
300: hsl(200 12% 95%),
|
||||
400: hsl(205 12% 88%),
|
||||
500: hsl(209 13% 83%),
|
||||
600: hsl(208 6% 55%),
|
||||
700: hsl(210 8% 31%),
|
||||
800: hsl(212 9% 22%),
|
||||
900: hsl(210 10% 14%),
|
||||
),
|
||||
dark: (
|
||||
100: hsl(240, 4%, 9%),
|
||||
),
|
||||
);
|
24
src/assets/scss/base/_container.scss
Normal file
24
src/assets/scss/base/_container.scss
Normal file
@@ -0,0 +1,24 @@
|
||||
// | -------------------------------------------------------------
|
||||
// | Container
|
||||
// | -------------------------------------------------------------
|
||||
|
||||
@use "breakpoint" as *;
|
||||
|
||||
.container {
|
||||
margin: 0 auto;
|
||||
padding: 0 calc(2rem / 2);
|
||||
max-width: 100%;
|
||||
|
||||
@include breakpoint(medium) {
|
||||
padding: 0 2rem;
|
||||
}
|
||||
|
||||
@include breakpoint(large) {
|
||||
padding: 0 calc(2rem / 2);
|
||||
max-width: 1200px;
|
||||
}
|
||||
|
||||
&.stretch {
|
||||
max-width: 100%;
|
||||
}
|
||||
}
|
149
src/assets/scss/base/_font.scss
Normal file
149
src/assets/scss/base/_font.scss
Normal file
@@ -0,0 +1,149 @@
|
||||
// | -------------------------------------------------------------
|
||||
// | Font
|
||||
// | -------------------------------------------------------------
|
||||
|
||||
@use 'breakpoint' as *;
|
||||
|
||||
@font-face {
|
||||
font-family: 'Open Sans';
|
||||
src: local('Open Sans ExtraBold'), local('OpenSans-ExtraBold'), url('/fonts/OpenSans-ExtraBold.woff2') format('woff2'),
|
||||
url('/fonts/OpenSans-ExtraBold.woff') format('woff');
|
||||
font-weight: bold;
|
||||
font-style: normal;
|
||||
font-display: swap;
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: 'Open Sans';
|
||||
src: local('Open Sans Bold'), local('OpenSans-Bold'), url('/fonts/OpenSans-Bold.woff2') format('woff2'),
|
||||
url('/fonts/OpenSans-Bold.woff') format('woff');
|
||||
font-weight: bold;
|
||||
font-style: normal;
|
||||
font-display: swap;
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: 'Open Sans';
|
||||
src: local('Open Sans Italic'), local('OpenSans-Italic'), url('/fonts/OpenSans-Italic.woff2') format('woff2'),
|
||||
url('/fonts/OpenSans-Italic.woff') format('woff');
|
||||
font-weight: normal;
|
||||
font-style: italic;
|
||||
font-display: swap;
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: 'Open Sans';
|
||||
src: local('Open Sans Regular'), local('OpenSans-Regular'), url('/fonts/OpenSans-Regular.woff2') format('woff2'),
|
||||
url('/fonts/OpenSans-Regular.woff') format('woff');
|
||||
font-weight: normal;
|
||||
font-style: normal;
|
||||
font-display: swap;
|
||||
}
|
||||
|
||||
body {
|
||||
font-family: var(--font-family-default);
|
||||
text-shadow: rgba(0, 0, 0, 0.01) 0 0 1px;
|
||||
text-rendering: optimizeLegibility;
|
||||
font-synthesis: none;
|
||||
font-size: 1rem;
|
||||
line-height: 1.5rem;
|
||||
-webkit-text-size-adjust: 100%;
|
||||
-moz-osx-font-smoothing: grayscale;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
|
||||
a:not(.button) {
|
||||
color: var(--action-color);
|
||||
text-decoration: underline;
|
||||
|
||||
&:visited {
|
||||
color: var(--action-color);
|
||||
}
|
||||
|
||||
&:hover,
|
||||
&:focus {
|
||||
color: var(--action-color-state);
|
||||
text-decoration: none;
|
||||
}
|
||||
}
|
||||
|
||||
:where(main) a {
|
||||
word-wrap: break-word;
|
||||
word-break: break-word;
|
||||
}
|
||||
|
||||
:where(h1, h2) {
|
||||
font-family: var(--font-family-special);
|
||||
}
|
||||
|
||||
h1,
|
||||
h2 {
|
||||
font-weight: 800;
|
||||
}
|
||||
|
||||
h3,
|
||||
h4,
|
||||
h5,
|
||||
h6 {
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
h1 {
|
||||
font-size: 2.25rem;
|
||||
line-height: 3.375rem;
|
||||
|
||||
@include breakpoint(medium) {
|
||||
font-size: 3rem;
|
||||
line-height: 3.625rem;
|
||||
}
|
||||
}
|
||||
|
||||
h2 {
|
||||
font-size: 1.875rem;
|
||||
line-height: 2.8125rem;
|
||||
|
||||
@include breakpoint(medium) {
|
||||
font-size: 2.25rem;
|
||||
line-height: 3.375rem;
|
||||
}
|
||||
}
|
||||
|
||||
h3 {
|
||||
font-size: 1.5rem;
|
||||
line-height: 2.25rem;
|
||||
|
||||
@include breakpoint(medium) {
|
||||
font-size: 1.875rem;
|
||||
line-height: 2.8125rem;
|
||||
}
|
||||
}
|
||||
|
||||
h4 {
|
||||
font-size: 1.25rem;
|
||||
line-height: 1.875rem;
|
||||
|
||||
@include breakpoint(medium) {
|
||||
font-size: 1.5rem;
|
||||
line-height: 2.25rem;
|
||||
}
|
||||
}
|
||||
|
||||
h5 {
|
||||
font-size: 1.125rem;
|
||||
line-height: 1.6875rem;
|
||||
|
||||
@include breakpoint(medium) {
|
||||
font-size: 1.25rem;
|
||||
line-height: 1.875rem;
|
||||
h6 {
|
||||
font-size: 1rem;
|
||||
}
|
||||
}
|
||||
|
||||
line-height: 1.5rem;
|
||||
|
||||
@include breakpoint(medium) {
|
||||
font-size: 1.125rem;
|
||||
line-height: 1.6875rem;
|
||||
}
|
||||
}
|
||||
}
|
67
src/assets/scss/base/_list.scss
Normal file
67
src/assets/scss/base/_list.scss
Normal file
@@ -0,0 +1,67 @@
|
||||
// | -------------------------------------------------------------
|
||||
// | Lists
|
||||
// | -------------------------------------------------------------
|
||||
|
||||
@use 'breakpoint' as *;
|
||||
|
||||
ul:not([class]),
|
||||
ol:not([class]) {
|
||||
margin-left: 1rem;
|
||||
|
||||
ul,
|
||||
ol {
|
||||
padding: 0.5rem 1rem 0;
|
||||
}
|
||||
|
||||
li {
|
||||
margin-bottom: 0.5rem;
|
||||
}
|
||||
}
|
||||
|
||||
ul:not([class]) {
|
||||
> li::marker {
|
||||
display: block;
|
||||
color: var(--primary-800);
|
||||
}
|
||||
}
|
||||
|
||||
ol.incremented {
|
||||
counter-reset: item;
|
||||
|
||||
ol {
|
||||
counter-reset: item;
|
||||
}
|
||||
|
||||
ol,
|
||||
ul {
|
||||
margin: 0.75rem 0 0 1rem;
|
||||
}
|
||||
|
||||
li {
|
||||
display: block;
|
||||
margin-bottom: 0.5rem;
|
||||
|
||||
@include breakpoint(medium) {
|
||||
margin-bottom: 0.75rem;
|
||||
}
|
||||
|
||||
&::before {
|
||||
content: counters(item, '.') '. ';
|
||||
counter-increment: item;
|
||||
}
|
||||
|
||||
&:last-child {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
p {
|
||||
display: inline;
|
||||
}
|
||||
}
|
||||
|
||||
ul {
|
||||
li::before {
|
||||
content: '';
|
||||
}
|
||||
}
|
||||
}
|
21
src/assets/scss/base/_outline.scss
Normal file
21
src/assets/scss/base/_outline.scss
Normal file
@@ -0,0 +1,21 @@
|
||||
// | -------------------------------------------------------------
|
||||
// | Outline
|
||||
// | -------------------------------------------------------------
|
||||
|
||||
@mixin outline {
|
||||
outline: 2px dotted black;
|
||||
outline-color: black;
|
||||
outline-offset: 0;
|
||||
-webkit-box-shadow: 0 0 0 2px white;
|
||||
box-shadow: 0 0 0 2px white;
|
||||
}
|
||||
|
||||
*:focus,
|
||||
*:focus-visible {
|
||||
@include outline;
|
||||
}
|
||||
|
||||
*:focus:not(:focus-visible) {
|
||||
outline: none;
|
||||
box-shadow: none;
|
||||
}
|
121
src/assets/scss/base/_reset.scss
Normal file
121
src/assets/scss/base/_reset.scss
Normal file
@@ -0,0 +1,121 @@
|
||||
// | -------------------------------------------------------------
|
||||
// | > Reset
|
||||
// | -------------------------------------------------------------
|
||||
|
||||
html {
|
||||
box-sizing: border-box;
|
||||
scroll-behavior: smooth;
|
||||
|
||||
@media (prefers-reduced-motion: reduce) {
|
||||
scroll-behavior: auto;
|
||||
|
||||
body * {
|
||||
animation-duration: 0s !important;
|
||||
animation-delay: 0s !important;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
*,
|
||||
*::after,
|
||||
*::before {
|
||||
box-sizing: inherit;
|
||||
}
|
||||
|
||||
blockquote,
|
||||
body,
|
||||
figure,
|
||||
h1,
|
||||
h2,
|
||||
h3,
|
||||
h4,
|
||||
h5,
|
||||
h6,
|
||||
hr,
|
||||
li,
|
||||
ol,
|
||||
p,
|
||||
pre,
|
||||
ul {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
ul:where([class]) {
|
||||
list-style: none;
|
||||
}
|
||||
|
||||
button,
|
||||
input,
|
||||
select,
|
||||
textarea {
|
||||
color: inherit;
|
||||
letter-spacing: inherit;
|
||||
font: inherit;
|
||||
}
|
||||
|
||||
input[type="text"],
|
||||
textarea {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
fieldset {
|
||||
padding: 0;
|
||||
border: none;
|
||||
}
|
||||
|
||||
legend {
|
||||
margin-bottom: 0.5rem;
|
||||
max-width: 100%;
|
||||
}
|
||||
|
||||
button,
|
||||
input,
|
||||
textarea {
|
||||
border: 1px solid gray;
|
||||
}
|
||||
|
||||
button {
|
||||
padding: 0.75em 1em;
|
||||
border-radius: 0;
|
||||
background-color: transparent;
|
||||
line-height: 1;
|
||||
}
|
||||
|
||||
button * {
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
button:hover {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
embed,
|
||||
iframe,
|
||||
img,
|
||||
object,
|
||||
svg,
|
||||
video {
|
||||
display: block;
|
||||
max-width: 100%;
|
||||
}
|
||||
|
||||
table {
|
||||
width: 100%;
|
||||
table-layout: fixed;
|
||||
}
|
||||
|
||||
[hidden] {
|
||||
display: none !important;
|
||||
}
|
||||
|
||||
noscript {
|
||||
display: block;
|
||||
margin-top: 1em;
|
||||
margin-bottom: 1em;
|
||||
}
|
||||
|
||||
[tabindex="-1"] {
|
||||
outline: none !important;
|
||||
box-shadow: none !important;
|
||||
}
|
13
src/assets/scss/base/_root.scss
Normal file
13
src/assets/scss/base/_root.scss
Normal file
@@ -0,0 +1,13 @@
|
||||
// | -------------------------------------------------------------
|
||||
// | Root
|
||||
// | -------------------------------------------------------------
|
||||
|
||||
@use "color" as *;
|
||||
|
||||
:root {
|
||||
@each $color, $shades in $colors {
|
||||
@each $shade, $value in $shades {
|
||||
--#{$color}-#{$shade}: #{$value};
|
||||
}
|
||||
}
|
||||
}
|
57
src/assets/scss/base/_space-content.scss
Normal file
57
src/assets/scss/base/_space-content.scss
Normal file
@@ -0,0 +1,57 @@
|
||||
// | -------------------------------------------------------------
|
||||
// | Space Content
|
||||
// | -------------------------------------------------------------
|
||||
|
||||
@use 'breakpoint' as *;
|
||||
|
||||
.space-content {
|
||||
> * + *,
|
||||
> dl > * + * {
|
||||
margin-top: 1.5rem;
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
> h2 {
|
||||
margin-top: 3rem;
|
||||
|
||||
@include breakpoint(large) {
|
||||
margin-top: 4rem;
|
||||
}
|
||||
}
|
||||
|
||||
> h3 {
|
||||
margin-top: 2rem;
|
||||
|
||||
@include breakpoint(large) {
|
||||
margin-top: 3rem;
|
||||
}
|
||||
}
|
||||
|
||||
> h4 {
|
||||
margin-top: 1.5rem;
|
||||
|
||||
@include breakpoint(large) {
|
||||
margin-top: 2rem;
|
||||
}
|
||||
}
|
||||
|
||||
> h5 {
|
||||
margin-top: 1rem;
|
||||
|
||||
@include breakpoint(large) {
|
||||
margin-top: 1.5rem;
|
||||
}
|
||||
}
|
||||
|
||||
> h6 {
|
||||
margin-top: 1rem;
|
||||
|
||||
@include breakpoint(large) {
|
||||
margin-top: 1.5rem;
|
||||
}
|
||||
}
|
||||
|
||||
> *:first-child {
|
||||
margin-top: 0;
|
||||
}
|
||||
}
|
14
src/assets/scss/globals.scss
Normal file
14
src/assets/scss/globals.scss
Normal file
@@ -0,0 +1,14 @@
|
||||
// | -------------------------------------------------------------
|
||||
// | Globals
|
||||
// | -------------------------------------------------------------
|
||||
|
||||
@use 'base/reset';
|
||||
@use 'base/root';
|
||||
@use 'base/font';
|
||||
@use 'base/list';
|
||||
@use 'base/container';
|
||||
@use 'base/breakpoint';
|
||||
@use 'base/button';
|
||||
@use 'base/color';
|
||||
@use 'base/outline';
|
||||
@use 'base/space-content';
|
30
src/components/CallToAction.astro
Normal file
30
src/components/CallToAction.astro
Normal file
@@ -0,0 +1,30 @@
|
||||
<div class="container">
|
||||
<div class="call-to-action mt-24 mb-32 flex flex-col items-center gap-12 rounded-xl p-12 md:p-24">
|
||||
<h2 class="text-center text-3xl md:text-5xl">Get this theme on GitHub</h2>
|
||||
<a href="https://github.com/markteekman/accessible-astro-starter" class="text-center text-lg"> Use this theme</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<style lang="scss">
|
||||
.call-to-action {
|
||||
color: var(--neutral-900);
|
||||
background-image: linear-gradient(40deg, var(--primary-100), var(--secondary-200));
|
||||
}
|
||||
|
||||
.call-to-action a:not(button),
|
||||
.call-to-action a:not(button):visited {
|
||||
padding: 1rem;
|
||||
color: var(--neutral-900);
|
||||
font-weight: bold;
|
||||
border: 3px solid var(--neutral-900);
|
||||
border-radius: 3px;
|
||||
text-decoration: none;
|
||||
transition: color 0.15s ease-in-out, background-color 0.15s ease-in-out;
|
||||
|
||||
&:where(:hover, :focus) {
|
||||
color: var(--neutral-100);
|
||||
background-color: var(--neutral-900);
|
||||
text-decoration: underline;
|
||||
}
|
||||
}
|
||||
</style>
|
17
src/components/ContentMedia.astro
Normal file
17
src/components/ContentMedia.astro
Normal file
@@ -0,0 +1,17 @@
|
||||
---
|
||||
import { Media } from 'accessible-astro-components'
|
||||
|
||||
const { imgSrc, reverseImg = false } = Astro.props
|
||||
---
|
||||
|
||||
<section class="my-64">
|
||||
<div class="container">
|
||||
<div class="grid grid-cols-1 gap-24 md:grid-cols-2">
|
||||
{!reverseImg ? <Media class="rounded-lg" src={imgSrc} /> : ''}
|
||||
<div class="space-content flex flex-col justify-center">
|
||||
<slot />
|
||||
</div>
|
||||
{reverseImg ? <Media class="rounded-lg" src={imgSrc} /> : ''}
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
15
src/components/Counter.astro
Normal file
15
src/components/Counter.astro
Normal file
@@ -0,0 +1,15 @@
|
||||
---
|
||||
const { count, title, sub } = Astro.props
|
||||
---
|
||||
|
||||
<div class="animate text-center">
|
||||
<p class="text-6xl font-bold">{count}</p>
|
||||
<p class="text-4xl font-semibold">{title}</p>
|
||||
<p class="text-2xl">{sub}</p>
|
||||
</div>
|
||||
|
||||
<style lang="scss">
|
||||
div > p:first-child {
|
||||
color: var(--action-color);
|
||||
}
|
||||
</style>
|
67
src/components/Feature.astro
Normal file
67
src/components/Feature.astro
Normal file
@@ -0,0 +1,67 @@
|
||||
---
|
||||
import { Icon } from 'astro-icon/components'
|
||||
|
||||
const { icon = 'mdi:rocket', title = 'Title' } = Astro.props
|
||||
---
|
||||
|
||||
<div class="feature flex flex-col gap-4">
|
||||
<Icon name={icon} />
|
||||
<div class="content">
|
||||
<h3>{title}</h3>
|
||||
<p>
|
||||
<slot>Lorem ipsum dolor sit amet consectetur adipisicing elit. Hic, corporis.</slot>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<style lang="scss">
|
||||
@use '../assets/scss/base/breakpoint' as *;
|
||||
|
||||
.feature {
|
||||
position: relative;
|
||||
width: calc(100% - 0.5rem);
|
||||
padding: 2rem;
|
||||
|
||||
> * {
|
||||
position: relative;
|
||||
z-index: 2;
|
||||
}
|
||||
|
||||
@include breakpoint(large) {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
&::before,
|
||||
&::after {
|
||||
content: '';
|
||||
position: absolute;
|
||||
}
|
||||
|
||||
&::before {
|
||||
inset: 0;
|
||||
background-color: var(--neutral-100);
|
||||
border: 3px solid var(--neutral-700);
|
||||
border-radius: 1rem;
|
||||
box-shadow: 0 0 0 6px var(--neutral-100);
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
&::after {
|
||||
background-color: var(--action-color);
|
||||
inset: 1rem -0.85rem -0.85rem 1rem;
|
||||
border-radius: 1rem;
|
||||
z-index: 0;
|
||||
}
|
||||
}
|
||||
|
||||
:global(.feature [data-icon]) {
|
||||
height: auto;
|
||||
width: 4rem;
|
||||
color: var(--action-color);
|
||||
}
|
||||
|
||||
:global(.darkmode .feature::before) {
|
||||
background-color: var(--dark-100);
|
||||
box-shadow: 0 0 0 6px var(--dark-100);
|
||||
}
|
||||
</style>
|
18
src/components/Footer.astro
Normal file
18
src/components/Footer.astro
Normal file
@@ -0,0 +1,18 @@
|
||||
---
|
||||
import CallToAction from './CallToAction.astro'
|
||||
|
||||
const currentYear = new Date().getFullYear()
|
||||
---
|
||||
|
||||
<footer>
|
||||
<CallToAction />
|
||||
<section class="py-8">
|
||||
<div class="container">
|
||||
<p>
|
||||
© {currentYear} - Starter Theme for <a href="https://astro.build/">Astro</a>. Made with ❤️ by <a
|
||||
href="https://github.com/markteekman">Mark Teekman</a
|
||||
>.
|
||||
</p>
|
||||
</div>
|
||||
</section>
|
||||
</footer>
|
65
src/components/Header.astro
Normal file
65
src/components/Header.astro
Normal file
@@ -0,0 +1,65 @@
|
||||
---
|
||||
import Navigation from '../components/Navigation.astro'
|
||||
import { SkipLinks } from 'accessible-astro-components'
|
||||
import { Icon } from 'astro-icon/components'
|
||||
---
|
||||
|
||||
<header>
|
||||
<SkipLinks />
|
||||
<Navigation>
|
||||
<li class="menu-item">
|
||||
<a href="/">Home</a>
|
||||
</li>
|
||||
<li class="menu-item">
|
||||
<a href="/blog/">Blog</a>
|
||||
</li>
|
||||
<li class="menu-item has-dropdown">
|
||||
<button aria-haspopup="true" aria-expanded="false">Example Pages</button>
|
||||
<ul class="dropdown-menu">
|
||||
<li class="submenu-item">
|
||||
<a href="/mdx-page/">MDX Page</a>
|
||||
</li>
|
||||
<li class="submenu-item">
|
||||
<a href="/markdown-page/">Markdown Page</a>
|
||||
</li>
|
||||
<li class="submenu-item">
|
||||
<a href="/accessible-components">Accessible Components</a>
|
||||
</li>
|
||||
</ul>
|
||||
</li>
|
||||
<li class="menu-item">
|
||||
<a href="https://accessible-astro.dev" title="external link" rel="external noopener noreferrer">External Link</a>
|
||||
</li>
|
||||
<li class="menu-item type-icon">
|
||||
<a href="https://github.com/markteekman/accessible-astro-starter" title="Go to the GitHub page">
|
||||
<Icon name="ion:logo-github" />
|
||||
</a>
|
||||
</li>
|
||||
</Navigation>
|
||||
</header>
|
||||
|
||||
<style lang="scss" is:global>
|
||||
@use '../assets/scss/base/outline' as *;
|
||||
|
||||
header {
|
||||
.type-icon a {
|
||||
display: block;
|
||||
|
||||
[data-icon] {
|
||||
height: auto;
|
||||
margin-top: -4px;
|
||||
width: 30px;
|
||||
|
||||
path {
|
||||
fill: var(--action-color);
|
||||
}
|
||||
}
|
||||
|
||||
&:hover {
|
||||
[data-icon] path {
|
||||
fill: var(--action-color-state);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
47
src/components/Hero.astro
Normal file
47
src/components/Hero.astro
Normal file
@@ -0,0 +1,47 @@
|
||||
---
|
||||
import { Icon } from 'astro-icon/components'
|
||||
|
||||
const { src = '/astronaut-hero-img.webp' } = Astro.props
|
||||
---
|
||||
|
||||
<section class="hero my-24">
|
||||
<div class="container">
|
||||
<div class="grid grid-cols-1 items-center gap-24 lg:grid-cols-2">
|
||||
<div class="flex flex-col items-center gap-8 md:items-start">
|
||||
<h1 class="text-center text-6xl md:text-left lg:text-8xl">
|
||||
<slot><span class="text-gradient">Accessible</span> Starter for Astro</slot>
|
||||
</h1>
|
||||
<div class="flex flex-col gap-3 min-[500px]:flex-row">
|
||||
<a class="button has-icon" href="https://github.com/markteekman/accessible-astro-starter">
|
||||
<Icon name="ion:star-outline" />
|
||||
Star on GitHub
|
||||
</a>
|
||||
<a
|
||||
class="button has-icon color-secondary"
|
||||
href="https://github.com/markteekman/accessible-astro-starter/blob/main/README.md"
|
||||
>
|
||||
<Icon name="ion:bookmark-outline" />
|
||||
Read the Docs
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
<img class="hidden lg:block" src={src} alt="" decoding="async" />
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<style lang="scss">
|
||||
.text-gradient {
|
||||
background: linear-gradient(315deg, var(--primary-200) 25%, var(--secondary-500));
|
||||
background-clip: border-box;
|
||||
-webkit-background-clip: text;
|
||||
-webkit-text-fill-color: transparent;
|
||||
}
|
||||
|
||||
:global(.darkmode .text-gradient) {
|
||||
background: linear-gradient(315deg, var(--primary-200) 25%, var(--secondary-400));
|
||||
background-clip: border-box;
|
||||
-webkit-background-clip: text;
|
||||
-webkit-text-fill-color: transparent;
|
||||
}
|
||||
</style>
|
415
src/components/Navigation.astro
Normal file
415
src/components/Navigation.astro
Normal file
@@ -0,0 +1,415 @@
|
||||
---
|
||||
import ResponsiveToggle from './ResponsiveToggle.astro'
|
||||
import { DarkMode } from 'accessible-astro-components'
|
||||
import { Image } from 'astro:assets'
|
||||
import logo from '../assets/img/logo.svg'
|
||||
---
|
||||
|
||||
<div id="main-navigation" class="is-desktop py-8">
|
||||
<div class="container">
|
||||
<a href="/" class="flex items-center gap-2 !no-underline">
|
||||
<Image src={logo} alt="Your Logo" width="47" height="37" />
|
||||
<span class="font-bold">Accessible Astro</span>
|
||||
</a>
|
||||
<div class="wrapper">
|
||||
<nav class="desktop-menu" aria-label="Main navigation desktop">
|
||||
<ul class="menu">
|
||||
<slot />
|
||||
</ul>
|
||||
</nav>
|
||||
<DarkMode />
|
||||
<ResponsiveToggle />
|
||||
</div>
|
||||
<nav class="mobile-menu" aria-label="Main navigation mobile">
|
||||
<ul class="menu">
|
||||
<slot />
|
||||
</ul>
|
||||
</nav>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
document.addEventListener('astro:page-load', () => {
|
||||
// variables
|
||||
const mainNav = document.querySelector('#main-navigation')
|
||||
const mainMenu = mainNav.querySelector('ul')
|
||||
const dropdownMenus = [...document.querySelectorAll('.has-dropdown button')]
|
||||
|
||||
// functions
|
||||
const setActiveMenuItem = () => {
|
||||
const mobileDesktopMenus = mainNav.querySelectorAll('nav > ul')
|
||||
const currenPathname = window.location.pathname
|
||||
|
||||
mobileDesktopMenus.forEach((menu) => {
|
||||
const menuItems = [...menu.querySelectorAll('a:not([rel*="external"])')] as HTMLAnchorElement[]
|
||||
|
||||
menuItems.forEach((menuItem) => {
|
||||
if (currenPathname.includes(menuItem.pathname.replaceAll('/', '')) && menuItem.textContent !== 'Home') {
|
||||
menuItem.classList.add('is-active')
|
||||
menuItem.setAttribute('aria-current', 'page')
|
||||
} else if (menuItem.textContent === 'Home' && currenPathname === '/') {
|
||||
menuItem.classList.add('is-active')
|
||||
menuItem.setAttribute('aria-current', 'page')
|
||||
}
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
const checkMenuSize = () => {
|
||||
const mainNavWidth = mainNav.getBoundingClientRect().width
|
||||
const desktopMenuWidth = document.querySelector('.desktop-menu').getBoundingClientRect().width
|
||||
const logoWidthBuffer = 300
|
||||
const totalMenuWidth = Math.round(desktopMenuWidth) + logoWidthBuffer
|
||||
|
||||
if (totalMenuWidth >= mainNavWidth) {
|
||||
mainNav.classList.remove('is-desktop')
|
||||
mainNav.classList.add('is-mobile')
|
||||
} else if (totalMenuWidth <= mainNavWidth) {
|
||||
mainNav.classList.add('is-desktop')
|
||||
mainNav.classList.remove('is-mobile')
|
||||
}
|
||||
}
|
||||
|
||||
const isOutOfViewport = (element) => {
|
||||
const elementBounds = element.getBoundingClientRect()
|
||||
return elementBounds.right > (window.innerWidth || document.documentElement.clientWidth)
|
||||
}
|
||||
|
||||
const openDropdownMenu = (dropdownMenu) => {
|
||||
const dropdownList = dropdownMenu.parentNode.querySelector('ul')
|
||||
|
||||
dropdownMenu.classList.add('show')
|
||||
dropdownMenu.setAttribute('aria-expanded', 'true')
|
||||
|
||||
if (isOutOfViewport(dropdownList)) {
|
||||
dropdownList.style.left = 'auto'
|
||||
}
|
||||
}
|
||||
|
||||
const closeDropdownMenu = (dropdownMenu) => {
|
||||
dropdownMenu.classList.remove('show')
|
||||
dropdownMenu.setAttribute('aria-expanded', 'false')
|
||||
}
|
||||
|
||||
const closeAllDropdownMenus = () => {
|
||||
for (let i = 0; i < dropdownMenus.length; i++) {
|
||||
closeDropdownMenu(dropdownMenus[i])
|
||||
}
|
||||
}
|
||||
|
||||
const toggleDropdownMenu = (event) => {
|
||||
if (event.target.getAttribute('aria-expanded') === 'false') {
|
||||
closeAllDropdownMenus()
|
||||
openDropdownMenu(event.target)
|
||||
} else {
|
||||
closeDropdownMenu(event.target)
|
||||
}
|
||||
}
|
||||
|
||||
// execution
|
||||
mainMenu &&
|
||||
mainMenu.addEventListener('keydown', (event) => {
|
||||
const element = event.target as Element
|
||||
const currentMenuItem = element.closest('li')
|
||||
const menuItems = [...mainMenu.querySelectorAll('.menu-item')]
|
||||
const currentDropdownMenu = element.closest('.has-dropdown button')
|
||||
const currentDropdownMenuItem = element.closest('.has-dropdown li')
|
||||
const currentIndex = menuItems.findIndex((item) => item === currentMenuItem)
|
||||
|
||||
const key = event.key
|
||||
let targetItem
|
||||
|
||||
if (key === 'ArrowRight') {
|
||||
if (menuItems.indexOf(currentMenuItem) === menuItems.length - 1) {
|
||||
targetItem = menuItems[0]
|
||||
} else {
|
||||
targetItem = menuItems[currentIndex + 1]
|
||||
}
|
||||
}
|
||||
|
||||
if (key === 'ArrowLeft') {
|
||||
if (menuItems.indexOf(currentMenuItem) === 0) {
|
||||
targetItem = menuItems[menuItems.length - 1]
|
||||
} else {
|
||||
targetItem = menuItems[currentIndex - 1]
|
||||
}
|
||||
}
|
||||
|
||||
if (key === 'Escape') {
|
||||
targetItem = menuItems[0]
|
||||
}
|
||||
|
||||
if (currentDropdownMenu) {
|
||||
const firstDropdownItem = currentDropdownMenu.nextElementSibling.querySelector('li')
|
||||
|
||||
if (key === 'ArrowDown') {
|
||||
event.preventDefault()
|
||||
openDropdownMenu(currentDropdownMenu)
|
||||
targetItem = firstDropdownItem
|
||||
}
|
||||
|
||||
if (key === 'Escape') {
|
||||
closeDropdownMenu(currentDropdownMenu)
|
||||
}
|
||||
}
|
||||
|
||||
if (currentDropdownMenuItem) {
|
||||
const currentDropdownList = currentDropdownMenuItem.parentNode
|
||||
const dropdownMenuItems = [...currentDropdownList.querySelectorAll('li')]
|
||||
const currentIndex = dropdownMenuItems.findIndex((item) => item === currentDropdownMenuItem)
|
||||
|
||||
if (key === 'ArrowDown') {
|
||||
event.preventDefault()
|
||||
|
||||
if (dropdownMenuItems.indexOf(currentDropdownMenuItem as HTMLLIElement) === dropdownMenuItems.length - 1) {
|
||||
targetItem = dropdownMenuItems[0]
|
||||
} else {
|
||||
targetItem = dropdownMenuItems[currentIndex + 1]
|
||||
}
|
||||
}
|
||||
|
||||
if (key === 'ArrowUp') {
|
||||
event.preventDefault()
|
||||
|
||||
if (dropdownMenuItems.indexOf(currentDropdownMenuItem as HTMLLIElement) === 0) {
|
||||
targetItem = dropdownMenuItems[dropdownMenuItems.length - 1]
|
||||
} else {
|
||||
targetItem = dropdownMenuItems[currentIndex - 1]
|
||||
}
|
||||
}
|
||||
|
||||
if (key === 'Escape') {
|
||||
const currentDropdownMenu = (currentDropdownList as Element).previousElementSibling
|
||||
targetItem = currentDropdownMenu.parentNode
|
||||
closeAllDropdownMenus()
|
||||
}
|
||||
|
||||
if (key === 'Tab') {
|
||||
const currentDropdownMenu = (currentDropdownList as Element).previousElementSibling
|
||||
|
||||
if (dropdownMenuItems.indexOf(currentDropdownMenuItem as HTMLLIElement) === dropdownMenuItems.length - 1) {
|
||||
closeDropdownMenu(currentDropdownMenu)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (targetItem) {
|
||||
targetItem.querySelector('a, button, input').focus()
|
||||
}
|
||||
})
|
||||
|
||||
dropdownMenus &&
|
||||
dropdownMenus.forEach((dropdownMenu) => {
|
||||
dropdownMenu.addEventListener('click', toggleDropdownMenu)
|
||||
})
|
||||
|
||||
setActiveMenuItem()
|
||||
checkMenuSize()
|
||||
|
||||
window.addEventListener('resize', checkMenuSize)
|
||||
window.addEventListener('click', (event) => {
|
||||
const element = event.target as Element
|
||||
if (!element.hasAttribute('aria-haspopup') && !element.classList.contains('submenu-item')) {
|
||||
closeAllDropdownMenus()
|
||||
}
|
||||
})
|
||||
})
|
||||
</script>
|
||||
|
||||
<style lang="scss" is:global>
|
||||
@use '../assets/scss/base/breakpoint' as *;
|
||||
@use '../assets/scss/base/outline' as *;
|
||||
|
||||
#main-navigation {
|
||||
> .container {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
&.is-desktop {
|
||||
.desktop-menu {
|
||||
visibility: visible;
|
||||
position: static;
|
||||
}
|
||||
|
||||
.mobile-menu {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.darkmode-toggle {
|
||||
margin-top: -6px;
|
||||
}
|
||||
}
|
||||
|
||||
&.is-mobile {
|
||||
flex-direction: column;
|
||||
|
||||
.mobile-menu {
|
||||
display: none;
|
||||
|
||||
&.show {
|
||||
display: block;
|
||||
}
|
||||
}
|
||||
|
||||
.desktop-menu {
|
||||
visibility: hidden;
|
||||
z-index: -99;
|
||||
position: absolute;
|
||||
left: 0;
|
||||
}
|
||||
|
||||
.responsive-toggle {
|
||||
display: block;
|
||||
}
|
||||
}
|
||||
|
||||
.wrapper {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 1rem;
|
||||
}
|
||||
|
||||
nav {
|
||||
> ul {
|
||||
display: flex;
|
||||
gap: 1.5rem;
|
||||
list-style-type: none;
|
||||
|
||||
a,
|
||||
button {
|
||||
text-decoration: none;
|
||||
font-size: 1.125rem;
|
||||
line-height: 1.6875rem;
|
||||
}
|
||||
|
||||
a:hover,
|
||||
a:focus,
|
||||
.is-active,
|
||||
.has-dropdown > button:hover,
|
||||
.has-dropdown > button:focus {
|
||||
text-decoration: underline;
|
||||
text-decoration-thickness: 1px;
|
||||
text-decoration-style: wavy;
|
||||
text-underline-offset: 7px;
|
||||
}
|
||||
|
||||
.is-active {
|
||||
font-weight: bold;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.mobile-menu {
|
||||
flex-basis: 100%;
|
||||
padding: 2rem 0;
|
||||
|
||||
> ul {
|
||||
flex-direction: column;
|
||||
|
||||
ul {
|
||||
position: relative;
|
||||
margin-top: 1rem;
|
||||
}
|
||||
}
|
||||
|
||||
a,
|
||||
button {
|
||||
display: block;
|
||||
width: 100%;
|
||||
padding: 0.5rem 0;
|
||||
}
|
||||
}
|
||||
|
||||
.has-dropdown {
|
||||
position: relative;
|
||||
|
||||
> button {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.5rem;
|
||||
padding: 0;
|
||||
margin-top: -1px;
|
||||
border: none;
|
||||
color: var(--action-color);
|
||||
|
||||
&:hover {
|
||||
color: var(--action-color-state);
|
||||
|
||||
&::after {
|
||||
border-color: var(--action-color-state);
|
||||
}
|
||||
}
|
||||
|
||||
&::after {
|
||||
content: '';
|
||||
width: 0.85rem;
|
||||
height: 0.75em;
|
||||
margin-top: -0.25rem;
|
||||
border-style: solid;
|
||||
border-width: 0.2em 0.2em 0 0;
|
||||
border-color: var(--action-color);
|
||||
transform: rotate(135deg);
|
||||
}
|
||||
|
||||
&.show {
|
||||
&::after {
|
||||
margin-top: 0.25rem;
|
||||
transform: rotate(-45deg);
|
||||
}
|
||||
|
||||
~ ul {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 1rem;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ul {
|
||||
display: none;
|
||||
position: absolute;
|
||||
z-index: 100;
|
||||
min-width: 260px;
|
||||
top: 125%;
|
||||
right: 0;
|
||||
bottom: auto;
|
||||
left: 0;
|
||||
padding: 1rem;
|
||||
background-color: var(--neutral-background);
|
||||
border: 2px solid black;
|
||||
box-shadow: 0 10px 20px rgba(0, 0, 0, 0.15);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.darkmode-toggle {
|
||||
padding: 0;
|
||||
border: none;
|
||||
|
||||
svg {
|
||||
width: 30px;
|
||||
margin-top: 4px;
|
||||
}
|
||||
|
||||
svg path {
|
||||
fill: var(--action-color);
|
||||
}
|
||||
|
||||
&:hover {
|
||||
svg path {
|
||||
fill: var(--action-color-state);
|
||||
}
|
||||
}
|
||||
|
||||
&:focus {
|
||||
@include outline;
|
||||
|
||||
&:not(:focus-visible) {
|
||||
outline: none;
|
||||
box-shadow: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
64
src/components/ResponsiveToggle.astro
Normal file
64
src/components/ResponsiveToggle.astro
Normal file
@@ -0,0 +1,64 @@
|
||||
<button class="responsive-toggle" aria-expanded="false" aria-label="Open menu navigation">
|
||||
<svg width="26" height="21" aria-hidden="true" fill="var(--action-color)" xmlns="http://www.w3.org/2000/svg"
|
||||
><path
|
||||
d="M2 1.667h24m-24 8h24m-24 8h24"
|
||||
stroke="var(--action-color)"
|
||||
stroke-width="2.667"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"></path></svg
|
||||
>
|
||||
</button>
|
||||
|
||||
<script>
|
||||
document.addEventListener('astro:page-load', () => {
|
||||
// variables
|
||||
const responsiveToggle = document.querySelector('.responsive-toggle')
|
||||
|
||||
// functions
|
||||
const openMenu = (toggle) => {
|
||||
toggle.setAttribute('aria-expanded', true)
|
||||
toggle.setAttribute('aria-label', 'Close menu navigation')
|
||||
toggle.innerHTML = `<svg width="20" height="20" aria-hidden="true" fill="var(--action-color)" xmlns="http://www.w3.org/2000/svg"><path d="M10 10 2 2m8 8 8 8m-8-8 8-8m-8 8-8 8" stroke="var(--action-color)" stroke-width="2.667" stroke-linecap="round" stroke-linejoin="round"/></svg>`
|
||||
}
|
||||
|
||||
const closeMenu = (toggle) => {
|
||||
toggle.setAttribute('aria-expanded', false)
|
||||
toggle.setAttribute('aria-label', 'Open menu navigation')
|
||||
toggle.innerHTML = `<svg width="26" height="21" aria-hidden="true" fill="var(--action-color)" xmlns="http://www.w3.org/2000/svg"><path d="M2 1.667h24m-24 8h24m-24 8h24" stroke="var(--action-color)" stroke-width="2.667" stroke-linecap="round" stroke-linejoin="round"/></svg>`
|
||||
}
|
||||
|
||||
// execution
|
||||
responsiveToggle.addEventListener('click', (_) => {
|
||||
const mobileNavigation = document.querySelector('.mobile-menu')
|
||||
|
||||
mobileNavigation.classList.toggle('show')
|
||||
mobileNavigation.classList.contains('show') ? openMenu(responsiveToggle) : closeMenu(responsiveToggle)
|
||||
})
|
||||
})
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
@use '../assets/scss/base/breakpoint' as *;
|
||||
@use '../assets/scss/base/outline' as *;
|
||||
|
||||
.responsive-toggle {
|
||||
display: none;
|
||||
padding: 0;
|
||||
margin-top: 6px;
|
||||
border: none;
|
||||
|
||||
svg {
|
||||
width: 30px;
|
||||
|
||||
path {
|
||||
transition: fill 0.2s ease-in-out;
|
||||
}
|
||||
}
|
||||
|
||||
&:hover {
|
||||
svg path {
|
||||
fill: var(--primary-400);
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
26
src/components/SiteMeta.astro
Normal file
26
src/components/SiteMeta.astro
Normal file
@@ -0,0 +1,26 @@
|
||||
---
|
||||
import { ViewTransitions } from 'astro:transitions'
|
||||
|
||||
const { title, description, url, image, author } = Astro.props
|
||||
|
||||
let subtitle = 'Accessible Astro Starter'
|
||||
---
|
||||
|
||||
<!-- general meta -->
|
||||
<meta name="title" content={`${title} - ${subtitle}`} />
|
||||
<meta name="description" content={description} />
|
||||
<meta name="author" content={author} />
|
||||
|
||||
<!-- open graph -->
|
||||
<meta property="og:title" content={`${title} - ${subtitle}`} />
|
||||
<meta property="og:description" content={description} />
|
||||
<meta property="og:type" content="website" />
|
||||
<meta property="og:url" content={url} />
|
||||
<meta property="og:image" content={Astro.site ? `${Astro.site}${image}` : image} />
|
||||
|
||||
<!-- twitter card -->
|
||||
|
||||
<!-- page title -->
|
||||
<title>{title} - {subtitle}</title>
|
||||
|
||||
<ViewTransitions />
|
2
src/env.d.ts
vendored
Normal file
2
src/env.d.ts
vendored
Normal file
@@ -0,0 +1,2 @@
|
||||
/// <reference path="../.astro/types.d.ts" />
|
||||
/// <reference types="astro/client" />
|
110
src/layouts/DefaultLayout.astro
Normal file
110
src/layouts/DefaultLayout.astro
Normal file
@@ -0,0 +1,110 @@
|
||||
---
|
||||
import '@astrojs/tailwind/base.css'
|
||||
import '../assets/scss/globals.scss'
|
||||
import SiteMeta from '../components/SiteMeta.astro'
|
||||
import Header from '../components/Header.astro'
|
||||
import Footer from '../components/Footer.astro'
|
||||
|
||||
const {
|
||||
title = 'Astro',
|
||||
description = 'A starter project with accessibility features using Astro static site builder. Contains accessible landmarks, skip links, a dark mode color scheme, better focus outline and keyboard menu navigation.',
|
||||
url,
|
||||
image = '/social-preview-image.png',
|
||||
author = 'Mark Teekman',
|
||||
} = Astro.props
|
||||
---
|
||||
|
||||
<html lang="en" dir="ltr">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<meta http-equiv="X-UA-Compatible" content="ie=edge" />
|
||||
|
||||
<!-- favicon -->
|
||||
<link rel="icon" type="image/svg+xml" href="/favicon.svg" />
|
||||
|
||||
<SiteMeta
|
||||
title={title}
|
||||
description={description.substring(0, 100)}
|
||||
url={Astro.site
|
||||
? `${Astro.site}/${title.toLowerCase().replaceAll(' ', '-')}`
|
||||
: `https://accessible-astro.dev/${title.toLowerCase().replaceAll(' ', '-')}`}
|
||||
image={image}
|
||||
author={author}
|
||||
/>
|
||||
</head>
|
||||
<body>
|
||||
<Header />
|
||||
<main id="main-content" transition:animate="fade">
|
||||
<slot />
|
||||
</main>
|
||||
<Footer />
|
||||
<style lang="scss" is:global>
|
||||
// theme settings
|
||||
:root {
|
||||
--radius-small: 3px;
|
||||
--radius-large: 6px;
|
||||
--gap-default: 2rem;
|
||||
--font-measure: 70ch;
|
||||
--font-family-default: 'Open Sans', sans-serif;
|
||||
--font-family-special: 'Open Sans', sans-serif;
|
||||
// light theme
|
||||
--font-color: var(--neutral-900);
|
||||
--action-color: var(--primary-300);
|
||||
--action-color-state: var(--primary-500);
|
||||
--background: var(--neutral-100);
|
||||
--primary-background: var(--primary-100);
|
||||
--neutral-background: var(--neutral-200);
|
||||
}
|
||||
|
||||
// dark color scheme overrides
|
||||
.darkmode {
|
||||
--font-color: var(--neutral-100);
|
||||
--action-color: var(--secondary-100);
|
||||
--action-color-state: var(--secondary-500);
|
||||
--background: var(--dark-100);
|
||||
--primary-background: var(--primary-500);
|
||||
--neutral-background: var(--neutral-900);
|
||||
}
|
||||
|
||||
// sticky footer on low content pages
|
||||
html,
|
||||
body {
|
||||
height: 100%;
|
||||
overflow-x: hidden;
|
||||
}
|
||||
|
||||
body {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
color: var(--font-color);
|
||||
background-color: var(--background);
|
||||
|
||||
main {
|
||||
flex: 1 0 auto;
|
||||
}
|
||||
|
||||
footer {
|
||||
flex-shrink: 0;
|
||||
}
|
||||
}
|
||||
|
||||
pre {
|
||||
padding: 1rem;
|
||||
border-radius: 0.35rem;
|
||||
border: 2px solid var(--action-color);
|
||||
}
|
||||
|
||||
body .pagination a {
|
||||
&:hover,
|
||||
&:focus-visible {
|
||||
background-color: var(--action-color-state);
|
||||
|
||||
svg path {
|
||||
stroke: white;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
</body>
|
||||
</html>
|
13
src/layouts/MarkdownLayout.astro
Normal file
13
src/layouts/MarkdownLayout.astro
Normal file
@@ -0,0 +1,13 @@
|
||||
---
|
||||
import DefaultLayout from './DefaultLayout.astro'
|
||||
|
||||
const { frontmatter } = Astro.props
|
||||
---
|
||||
|
||||
<DefaultLayout title={frontmatter.title}>
|
||||
<div class="container">
|
||||
<div class="space-content my-12">
|
||||
<slot />
|
||||
</div>
|
||||
</div>
|
||||
</DefaultLayout>
|
17
src/pages/404.astro
Normal file
17
src/pages/404.astro
Normal file
@@ -0,0 +1,17 @@
|
||||
---
|
||||
import DefaultLayout from '../layouts/DefaultLayout.astro'
|
||||
---
|
||||
|
||||
<DefaultLayout title="404">
|
||||
<section class="my-12">
|
||||
<div class="container">
|
||||
<h1>404</h1>
|
||||
</div>
|
||||
</section>
|
||||
<section class="my-12">
|
||||
<div class="space-content container">
|
||||
<p class="text-2xl">This page does not exist. Don't worry though, we got you.</p>
|
||||
<a class="button" href="/">Let's get you home Astronout 🚀</a>
|
||||
</div>
|
||||
</section>
|
||||
</DefaultLayout>
|
150
src/pages/accessible-components.astro
Normal file
150
src/pages/accessible-components.astro
Normal file
@@ -0,0 +1,150 @@
|
||||
---
|
||||
import DefaultLayout from '../layouts/DefaultLayout.astro'
|
||||
import { Icon } from 'astro-icon/components'
|
||||
import {
|
||||
Accordion,
|
||||
AccordionItem,
|
||||
Breadcrumbs,
|
||||
BreadcrumbsItem,
|
||||
Card,
|
||||
Media,
|
||||
Modal,
|
||||
Notification,
|
||||
Pagination,
|
||||
} from 'accessible-astro-components'
|
||||
---
|
||||
|
||||
<DefaultLayout title="Accessible Components">
|
||||
<section class="my-12">
|
||||
<div class="space-content container">
|
||||
<h1>Accessible Components</h1>
|
||||
<p class="text-2xl">
|
||||
This theme has a lot of extra a11y components provided by the <a
|
||||
href="https://github.com/markteekman/accessible-astro-components#Pagination">Accessible Astro Component</a
|
||||
> NPM package to help you build accessible pages faster:
|
||||
</p>
|
||||
</div>
|
||||
</section>
|
||||
<section class="mb-12 mt-32">
|
||||
<div class="container">
|
||||
<div class="grid grid-cols-1 gap-32 md:grid-cols-2">
|
||||
<div class="space-content">
|
||||
<h2>Accordion</h2>
|
||||
<Accordion>
|
||||
<AccordionItem header="First Item">
|
||||
<p>
|
||||
Lorem ipsum dolor sit amet consectetur adipisicing elit. Architecto quasi nobis optio? Qui in quo
|
||||
accusantium debitis sapiente obcaecati magnam incidunt sit. Molestiae exercitationem quibusdam quod
|
||||
veritatis laboriosam est tenetur.
|
||||
</p>
|
||||
<a href="#">Tab to me!</a>
|
||||
</AccordionItem>
|
||||
<AccordionItem header="Second Item">
|
||||
<p>
|
||||
Lorem ipsum dolor sit amet consectetur adipisicing elit. Architecto quasi nobis optio? Qui in quo
|
||||
accusantium debitis sapiente obcaecati magnam incidunt sit. Molestiae exercitationem quibusdam quod
|
||||
veritatis laboriosam est tenetur.
|
||||
</p>
|
||||
</AccordionItem>
|
||||
<AccordionItem header="Third Item">
|
||||
<p>
|
||||
Lorem ipsum dolor sit amet consectetur adipisicing elit. Architecto quasi nobis optio? Qui in quo
|
||||
accusantium debitis sapiente obcaecati magnam incidunt sit. Molestiae exercitationem quibusdam quod
|
||||
veritatis laboriosam est tenetur.
|
||||
</p>
|
||||
</AccordionItem>
|
||||
<AccordionItem header="Fourth Item">
|
||||
<p>
|
||||
Lorem ipsum dolor sit amet consectetur adipisicing elit. Architecto quasi nobis optio? Qui in quo
|
||||
accusantium debitis sapiente obcaecati magnam incidunt sit. Molestiae exercitationem quibusdam quod
|
||||
veritatis laboriosam est tenetur.
|
||||
</p>
|
||||
</AccordionItem>
|
||||
<AccordionItem header="Fifth Item">
|
||||
<p>
|
||||
Lorem ipsum dolor sit amet consectetur adipisicing elit. Architecto quasi nobis optio? Qui in quo
|
||||
accusantium debitis sapiente obcaecati magnam incidunt sit. Molestiae exercitationem quibusdam quod
|
||||
veritatis laboriosam est tenetur.
|
||||
</p>
|
||||
</AccordionItem>
|
||||
</Accordion>
|
||||
</div>
|
||||
<div class="space-content">
|
||||
<h2>Breadcrumbs</h2>
|
||||
<Breadcrumbs>
|
||||
<BreadcrumbsItem href="/" label="Home" />
|
||||
<BreadcrumbsItem href="/blog" label="Blog" />
|
||||
<BreadcrumbsItem currentPage={true} label="My blog post" />
|
||||
</Breadcrumbs>
|
||||
</div>
|
||||
<div class="space-content">
|
||||
<h2>Cards</h2>
|
||||
<Card
|
||||
img="https://images.unsplash.com/photo-1636819488524-1f019c4e1c44?ixlib=rb-4.0.3&ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&auto=format&fit=crop&w=800&q=80"
|
||||
/>
|
||||
</div>
|
||||
<div class="space-content">
|
||||
<h2>DarkMode Toggle</h2>
|
||||
<p>This component can be viewed in the top right corner besides the main navigation.</p>
|
||||
</div>
|
||||
<div class="space-content">
|
||||
<h2>Media</h2>
|
||||
<Media
|
||||
class="rounded-lg"
|
||||
src="https://images.unsplash.com/photo-1636819488537-a9b1ffb315ce?ixlib=rb-4.0.3&ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&auto=format&fit=crop&w=800&q=80"
|
||||
/>
|
||||
</div>
|
||||
<div class="space-content">
|
||||
<h2>Modals</h2>
|
||||
<button id="modal1-trigger" class="button">Modal 1</button>
|
||||
<button id="modal2-trigger" class="button color-secondary">Modal 2</button>
|
||||
<Modal triggerId="modal1-trigger" title="Modal 1">
|
||||
<p>Why hello, I be the <strong>first</strong> Modal.</p>
|
||||
</Modal>
|
||||
<Modal triggerId="modal2-trigger" title="Modal 2" closeText="Cancel">
|
||||
<p>
|
||||
Ah yes, and I be the <strong>second</strong> Modal. Lorem ipsum dolor sit amet, consectetur adipisicing elit.
|
||||
Sed, a! Ratione eaque temporibus alias tempora pariatur dolorem.
|
||||
</p>
|
||||
<button class="button" onclick="closeModal()">Confirm action</button>
|
||||
</Modal>
|
||||
</div>
|
||||
<div class="space-content">
|
||||
<h2>Notification</h2>
|
||||
<Notification>
|
||||
<Icon name="ion:notifications-outline" /><p><strong>Message:</strong> This is a notification!</p>
|
||||
</Notification>
|
||||
<Notification type="info">
|
||||
<Icon name="ion:information-circle-outline" /><p>
|
||||
<strong>Info:</strong> This is a notification of type info.
|
||||
</p>
|
||||
</Notification>
|
||||
<Notification type="success">
|
||||
<Icon name="ion:checkbox-outline" /><p>
|
||||
<strong>Success:</strong> This is a notification of type success.
|
||||
</p>
|
||||
</Notification>
|
||||
<Notification type="warning">
|
||||
<Icon name="ion:warning-outline" /><p>
|
||||
<strong>Warning:</strong> This is a notification of type warning and goes on multiple lines to see how that
|
||||
looks.
|
||||
</p>
|
||||
</Notification>
|
||||
<Notification type="error">
|
||||
<Icon name="ion:alert-circle-outline" /><p>
|
||||
<strong>Error:</strong> This is a notification of type error.
|
||||
</p>
|
||||
</Notification>
|
||||
</div>
|
||||
<div class="space-content">
|
||||
<h2>Pagination</h2>
|
||||
<Pagination totalPages="200" />
|
||||
</div>
|
||||
<div class="space-content">
|
||||
<h2>SkipLinks</h2>
|
||||
<p>This component can be viewed by shift+tabbing until they show up.</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
</DefaultLayout>
|
76
src/pages/blog/[...page].astro
Normal file
76
src/pages/blog/[...page].astro
Normal file
@@ -0,0 +1,76 @@
|
||||
---
|
||||
import DefaultLayout from '../../layouts/DefaultLayout.astro'
|
||||
import { Card, Pagination } from 'accessible-astro-components'
|
||||
|
||||
export async function getStaticPaths({ paginate }) {
|
||||
const response = await fetch('https://jsonplaceholder.typicode.com/posts')
|
||||
const data = await response.json()
|
||||
|
||||
return paginate(data, { pageSize: 6 })
|
||||
}
|
||||
|
||||
const { page } = Astro.props
|
||||
---
|
||||
|
||||
<DefaultLayout
|
||||
title="Blog"
|
||||
description="An example of a blog with dynamic content fetched from JSONPlaceholder using the title, body and userId."
|
||||
>
|
||||
<section class="my-12">
|
||||
<div class="space-content container">
|
||||
<h1>Blog</h1>
|
||||
<p class="text-2xl">
|
||||
An example of a blog with dynamic content fetched from <a href="https://jsonplaceholder.typicode.com/posts"
|
||||
>JSONPlaceholder</a
|
||||
> using the title, body and userId. The Accessible Astro Card Component is used here to display al the posts.
|
||||
</p>
|
||||
</div>
|
||||
</section>
|
||||
<section class="my-12">
|
||||
<div class="container">
|
||||
<p class="text-sm"><em>Post {page.start + 1} through {page.end + 1} of {page.total} total posts</em></p>
|
||||
<ul class="my-3">
|
||||
{
|
||||
page.data.map((post) => (
|
||||
<li>
|
||||
<Card
|
||||
url={'/blog/' + post.title.replaceAll(' ', '-').toLowerCase()}
|
||||
title={post.title}
|
||||
footer={'userId:' + post.userId}
|
||||
>
|
||||
{post.body}
|
||||
</Card>
|
||||
</li>
|
||||
))
|
||||
}
|
||||
</ul>
|
||||
<div class="mt-12 grid place-content-center">
|
||||
<Pagination
|
||||
firstPage={page.url.prev ? '/blog' : null}
|
||||
previousPage={page.url.prev ? page.url.prev : null}
|
||||
nextPage={page.url.next ? page.url.next : null}
|
||||
lastPage={page.url.next ? `/blog/${Math.round(page.total / page.size)}` : null}
|
||||
currentPage={page.currentPage}
|
||||
totalPages={Math.round(page.total / page.size)}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
</DefaultLayout>
|
||||
|
||||
<style lang="scss">
|
||||
ul {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr;
|
||||
grid-gap: 4rem;
|
||||
|
||||
@media (min-width: 550px) {
|
||||
grid-template-columns: repeat(2, 1fr);
|
||||
grid-gap: 2rem;
|
||||
}
|
||||
|
||||
@media (min-width: 950px) {
|
||||
grid-template-columns: repeat(3, 1fr);
|
||||
}
|
||||
}
|
||||
</style>
|
57
src/pages/blog/[post].astro
Normal file
57
src/pages/blog/[post].astro
Normal file
@@ -0,0 +1,57 @@
|
||||
---
|
||||
import DefaultLayout from '../../layouts/DefaultLayout.astro'
|
||||
import { Breadcrumbs, BreadcrumbsItem } from 'accessible-astro-components'
|
||||
|
||||
export async function getStaticPaths() {
|
||||
const data = await fetch('https://jsonplaceholder.typicode.com/posts').then((response) => response.json())
|
||||
|
||||
return data.map((post) => {
|
||||
return {
|
||||
params: { post: post.title.replaceAll(' ', '-').toLowerCase() },
|
||||
props: { post },
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
const { post } = Astro.props
|
||||
---
|
||||
|
||||
<DefaultLayout title={post.title} description={post.body} url={post.title}>
|
||||
<div class="container">
|
||||
<div class="mt-12">
|
||||
<Breadcrumbs>
|
||||
<BreadcrumbsItem href="/" label="Home" />
|
||||
<BreadcrumbsItem href="/blog" label="Blog" />
|
||||
<BreadcrumbsItem currentPage={true} label={post.title} />
|
||||
</Breadcrumbs>
|
||||
</div>
|
||||
</div>
|
||||
<section class="my-12">
|
||||
<div class="container">
|
||||
<h1>{post.title}</h1><br />
|
||||
<p>By userId: {post.userId}</p>
|
||||
</div>
|
||||
</section>
|
||||
<section class="my-12">
|
||||
<div class="container">
|
||||
<p class="text-2xl">{post.body}</p>
|
||||
</div>
|
||||
</section>
|
||||
</DefaultLayout>
|
||||
|
||||
<style lang="scss">
|
||||
ul {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr;
|
||||
grid-gap: 4rem;
|
||||
|
||||
@media (min-width: 550px) {
|
||||
grid-template-columns: repeat(2, 1fr);
|
||||
grid-gap: 2rem;
|
||||
}
|
||||
|
||||
@media (min-width: 950px) {
|
||||
grid-template-columns: repeat(3, 1fr);
|
||||
}
|
||||
}
|
||||
</style>
|
73
src/pages/index.astro
Normal file
73
src/pages/index.astro
Normal file
@@ -0,0 +1,73 @@
|
||||
---
|
||||
import DefaultLayout from '../layouts/DefaultLayout.astro'
|
||||
import Hero from '../components/Hero.astro'
|
||||
import Feature from '../components/Feature.astro'
|
||||
import Counter from '../components/Counter.astro'
|
||||
import ContentMedia from '../components/ContentMedia.astro'
|
||||
---
|
||||
|
||||
<DefaultLayout title="Home">
|
||||
<Hero />
|
||||
<section class="my-64">
|
||||
<div class="container">
|
||||
<h2 class="mb-16 text-6xl">Features</h2>
|
||||
<div class="grid grid-cols-1 gap-12 md:grid-cols-2 lg:grid-cols-3">
|
||||
<Feature icon="mdi:tailwind" title="Tailwind CSS">
|
||||
Use the power of Tailwind to greatly improve your productivity and enhance your developer workflow.
|
||||
</Feature>
|
||||
<Feature icon="mdi:wheelchair-accessibility" title="a11y Components">
|
||||
17 components and counting, all tried and tested for the most optimal accessible experience for your visitors.
|
||||
</Feature>
|
||||
<Feature icon="mdi:looks" title="Prettier">
|
||||
Less worry about formatting your code, let the Astro Prettier integration do the heavy lifting.
|
||||
</Feature>
|
||||
<Feature icon="mdi:eslint" title="ESLint">
|
||||
Lint your code with strict a11y settings to ensure you stay on track with the WCAG standards.
|
||||
</Feature>
|
||||
<Feature icon="mdi:book-open-page-variant" title="Blog">
|
||||
This theme comes with a fully integrated blog, dynamic pages and SEO optimization.
|
||||
</Feature>
|
||||
<Feature icon="mdi:language-markdown" title="Markdown & MDX">
|
||||
Easily use .md and .mdx pages to build your websites or use it with Netlify CMS.
|
||||
</Feature>
|
||||
<Feature icon="mdi:theme-light-dark" title="Dark mode">
|
||||
Fully integrated Dark mode gives your users the choice for their favorite viewing mode.
|
||||
</Feature>
|
||||
<Feature icon="mdi:cog" title="Design System">
|
||||
The theme offers some very handy utilities to help you build your website faster.
|
||||
</Feature>
|
||||
<Feature icon="mdi:lighthouse" title="Lighthouse Scores">
|
||||
Excellent lighthouse scores means your website will load faster and get better SEO rankings.
|
||||
</Feature>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
<ContentMedia imgSrc="/accessible-components.webp">
|
||||
<h2>Accessible Components</h2>
|
||||
<p class="text-2xl">
|
||||
This theme provides plenty of tried and tested Accessible Astro Components. Some are native to this theme and a
|
||||
lot of others are integrated using a <a href="https://github.com/markteekman/accessible-astro-components"
|
||||
>separate package</a
|
||||
>. They'll get you up and running in building an accessible solution for your visitors.
|
||||
</p>
|
||||
</ContentMedia>
|
||||
<ContentMedia imgSrc="/wcag-compliant.webp" reverseImg={true}>
|
||||
<h2>WCAG 2.1 AA Compliant</h2>
|
||||
<p class="text-2xl">
|
||||
Using semantic HTML, landmarks, skip links, screen reader friendly content, aria-labels, keyboard accessible
|
||||
navigation and components, clear outlines and tab indicators and the right color contrast, you're more certain of
|
||||
reaching WCAG AA compliance.
|
||||
</p>
|
||||
</ContentMedia>
|
||||
<section class="mb-32 mt-64">
|
||||
<div class="container">
|
||||
<h2 class="mb-16 text-6xl">Counters</h2>
|
||||
<div class="grid grid-cols-1 gap-12 sm:grid-cols-2 md:grid-cols-4">
|
||||
<Counter count="520" title="Stars" sub="On GitHub" />
|
||||
<Counter count="17" title="Accessible" sub="Components" />
|
||||
<Counter count="500" title="Commits" sub="Merged" />
|
||||
<Counter count="18+" title="Months" sub="Since launch" />
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
</DefaultLayout>
|
16
src/pages/markdown-page.md
Normal file
16
src/pages/markdown-page.md
Normal file
@@ -0,0 +1,16 @@
|
||||
---
|
||||
layout: ../layouts/MarkdownLayout.astro
|
||||
title: Markdown Page
|
||||
---
|
||||
|
||||
# Markdown Page
|
||||
|
||||
Lorem ipsum dolor sit amet consectetur adipisicing elit. Vitae veniam repellat deleniti obcaecati facilis non, praesentium aperiam laudantium excepturi assumenda doloremque animi quis aliquam eligendi quia nemo asperiores et eaque, sunt voluptatibus, saepe exercitationem id. Quis sequi maxime fugiat nam reprehenderit nesciunt quaerat obcaecati, ipsa dignissimos voluptatum voluptatem, optio quidem quos repudiandae dolorem voluptatibus fuga officia odio nemo recusandae voluptas.
|
||||
|
||||
```js
|
||||
console.log('Hello Accessible World!')
|
||||
```
|
||||
|
||||
Lorem ipsum dolor sit amet consectetur adipisicing elit. Vitae veniam repellat deleniti obcaecati facilis non, praesentium aperiam laudantium excepturi assumenda doloremque animi quis aliquam eligendi quia nemo asperiores et eaque, sunt voluptatibus, saepe exercitationem id. Quis sequi maxime fugiat nam reprehenderit nesciunt quaerat obcaecati, ipsa dignissimos voluptatum voluptatem, optio quidem quos repudiandae dolorem voluptatibus fuga officia odio nemo recusandae voluptas.
|
||||
|
||||
[Get this theme on GitHub](https://github.com/markteekman/accessible-astro-starter)
|
27
src/pages/mdx-page.mdx
Normal file
27
src/pages/mdx-page.mdx
Normal file
@@ -0,0 +1,27 @@
|
||||
---
|
||||
layout: ../layouts/MarkdownLayout.astro
|
||||
title: MDX Page
|
||||
---
|
||||
|
||||
import { Icon } from 'astro-icon/components'
|
||||
import { Notification } from 'accessible-astro-components'
|
||||
|
||||
# MDX Page
|
||||
|
||||
<Notification type="info">
|
||||
<Icon name="ion:information-circle-outline" />
|
||||
<p>
|
||||
<strong>Info:</strong> This page utilizes Astro's MDX feature which let's you use components in a markdown file and
|
||||
supports out-of-the-box syntax highlighting with Shiki.
|
||||
</p>
|
||||
</Notification>
|
||||
|
||||
Lorem ipsum dolor sit amet consectetur adipisicing elit. Vitae veniam repellat deleniti obcaecati facilis non, praesentium aperiam laudantium excepturi assumenda doloremque animi quis aliquam eligendi quia nemo asperiores et eaque, sunt voluptatibus, saepe exercitationem id. Quis sequi maxime fugiat nam reprehenderit nesciunt quaerat obcaecati, ipsa dignissimos voluptatum voluptatem, optio quidem quos repudiandae dolorem voluptatibus fuga officia odio nemo recusandae voluptas.
|
||||
|
||||
```js
|
||||
console.log('Hello Accessible World!')
|
||||
```
|
||||
|
||||
Lorem ipsum dolor sit amet consectetur adipisicing elit. Vitae veniam repellat deleniti obcaecati facilis non, praesentium aperiam laudantium excepturi assumenda doloremque animi quis aliquam eligendi quia nemo asperiores et eaque, sunt voluptatibus, saepe exercitationem id. Quis sequi maxime fugiat nam reprehenderit nesciunt quaerat obcaecati, ipsa dignissimos voluptatum voluptatem, optio quidem quos repudiandae dolorem voluptatibus fuga officia odio nemo recusandae voluptas.
|
||||
|
||||
[Get this theme on GitHub](https://github.com/markteekman/accessible-astro-starter)
|
9
tailwind.config.js
Normal file
9
tailwind.config.js
Normal file
@@ -0,0 +1,9 @@
|
||||
/** @type {import('tailwindcss').Config} */
|
||||
module.exports = {
|
||||
content: ['./src/**/*.{astro,html,js,jsx,md,mdx,svelte,ts,tsx,vue}'],
|
||||
theme: {
|
||||
extend: {},
|
||||
},
|
||||
plugins: [],
|
||||
darkMode: ['class', '.darkmode'],
|
||||
}
|
Reference in New Issue
Block a user