init
This commit is contained in:
commit
2c1f2adcf8
28 changed files with 4155 additions and 0 deletions
2
.gitignore
vendored
Normal file
2
.gitignore
vendored
Normal file
|
@ -0,0 +1,2 @@
|
|||
node_modules/
|
||||
.next/
|
21
LICENSE
Normal file
21
LICENSE
Normal file
|
@ -0,0 +1,21 @@
|
|||
MIT License
|
||||
|
||||
Copyright (c) 2022 sup39
|
||||
|
||||
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.
|
5
README.md
Normal file
5
README.md
Normal file
|
@ -0,0 +1,5 @@
|
|||
# supMDX
|
||||
A template to make blog-like site with MDX
|
||||
|
||||
## Configuration
|
||||
- `supMDX.yml`
|
10
components/Footer.tsx
Normal file
10
components/Footer.tsx
Normal file
|
@ -0,0 +1,10 @@
|
|||
import config from '#config';
|
||||
|
||||
export default function Footer() {
|
||||
const {startYear: year0, siteAuthor} = config;
|
||||
const year = new Date().getFullYear();
|
||||
|
||||
return <footer>{siteAuthor &&
|
||||
<div>Copyright © {year>year0 ? `${year0}-${year}` : year} {siteAuthor} All rights reserved.</div>
|
||||
}</footer>;
|
||||
}
|
35
components/MDXRoot.tsx
Normal file
35
components/MDXRoot.tsx
Normal file
|
@ -0,0 +1,35 @@
|
|||
import Head from 'next/head';
|
||||
import Nav from './Nav';
|
||||
import MetaInfo from './MetaInfo';
|
||||
import type {HeadingInfo} from '@sup39/rehype-mdx-export-headings';
|
||||
|
||||
export type MDXProps = {
|
||||
children: JSX.Element
|
||||
data: {
|
||||
pathname: string,
|
||||
},
|
||||
meta: Partial<{
|
||||
title: string
|
||||
description: string
|
||||
h1: string
|
||||
[key: string]: any
|
||||
}>
|
||||
headings: HeadingInfo[]
|
||||
};
|
||||
|
||||
export default function MDXRoot({children, data: {pathname}, meta={}, headings}: MDXProps) {
|
||||
const {title, description} = meta;
|
||||
const h1 = meta.h1 ?? title;
|
||||
return <>
|
||||
<Head>
|
||||
<title>{title}</title>
|
||||
{description && <meta name="description" content={description} />}
|
||||
</Head>
|
||||
<Nav pathname={pathname} headings={headings} />
|
||||
<main>
|
||||
{h1 ? <h1>{h1}</h1> : <></>}
|
||||
<MetaInfo data={meta} />
|
||||
{children}
|
||||
</main>
|
||||
</>;
|
||||
}
|
12
components/MetaInfo.tsx
Normal file
12
components/MetaInfo.tsx
Normal file
|
@ -0,0 +1,12 @@
|
|||
import config from '#config';
|
||||
|
||||
export default function MetaInfo({data}: {data: {[_: string]: any}}) {
|
||||
const {metaFields: fields = []} = config;
|
||||
return <div>{fields.map(({label, prop}) => {
|
||||
const val = data[prop];
|
||||
return val == null ? null : <div key={prop}>
|
||||
<span>{label}</span>
|
||||
<span>{val}</span>
|
||||
</div>;
|
||||
})}</div>;
|
||||
}
|
53
components/Nav.tsx
Normal file
53
components/Nav.tsx
Normal file
|
@ -0,0 +1,53 @@
|
|||
import {useState} from 'react';
|
||||
import Link from 'next/link';
|
||||
import NavHeader from './NavHeader';
|
||||
import type {HeadingInfo} from '@sup39/rehype-mdx-export-headings';
|
||||
import config from '#config';
|
||||
|
||||
export type NavEntryInfo = {
|
||||
name: string
|
||||
link: string
|
||||
children?: NavEntryInfo[]|null
|
||||
};
|
||||
|
||||
export function NavEntry<Body, >({
|
||||
entry: {name, link, children}, dir, here, children: childrenJSX,
|
||||
}: {entry: NavEntryInfo, dir: string, here: string, children?: Body}) {
|
||||
const [toggle, setToggle] = useState(false);
|
||||
const href = dir+link;
|
||||
const isHere = href.replace(/\/$/, '')===here; // remove trailing slash
|
||||
const entryCls = 'nav-entry'+(isHere ? ' nav-here' : '');
|
||||
return children?.length ? <div className={'nav-dir'+(toggle ? ' nav-fold-open' : '')}><>
|
||||
<div className={entryCls}>
|
||||
<Link href={href}>{name}</Link>
|
||||
<svg viewBox="0 0 8 8" onClick={()=>setToggle(e=>!e)}><polyline points="6 3 4 5 2 3"></polyline></svg>
|
||||
</div>
|
||||
{isHere ? childrenJSX : <></>}
|
||||
<div className='nav-dir-child'>{
|
||||
children.map(entry => <NavEntry key={entry.link} entry={entry} dir={href} here={here}>{childrenJSX}</NavEntry>)
|
||||
}</div>
|
||||
</></div> : <div className={entryCls}>
|
||||
<Link href={href}>{name}</Link>
|
||||
</div>;
|
||||
}
|
||||
|
||||
export default function Nav({children, headings, pathname}: {
|
||||
children?: JSX.Element
|
||||
headings: HeadingInfo[]
|
||||
pathname: string
|
||||
}) {
|
||||
const [navFold, setNavFold] = useState(false);
|
||||
|
||||
const headingsJSX = <ul className=''>{headings.map(({label, id}) => <li key={id}>
|
||||
<a href={'#'+id}>{label}</a>
|
||||
</li>)}</ul>;
|
||||
|
||||
return <nav className={navFold ? 'open' : ''}>
|
||||
<NavHeader onToggleFold={()=>setNavFold(e=>!e)} />
|
||||
{children}
|
||||
<div className='nav-root'>
|
||||
{config.nav.map(entry => <NavEntry key={entry.link} entry={entry} dir={'/'} here={pathname} />)}
|
||||
{headingsJSX}
|
||||
</div>
|
||||
</nav>;
|
||||
}
|
10
components/NavHeader.tsx
Normal file
10
components/NavHeader.tsx
Normal file
|
@ -0,0 +1,10 @@
|
|||
import Link from 'next/link';
|
||||
|
||||
export default function NavHeader({onToggleFold}: {onToggleFold?: ()=>void}) {
|
||||
return <header>
|
||||
<Link href="/" id="icon-link">
|
||||
<div style={{fontSize: '1.5em'}}>supMDX</div>
|
||||
</Link>
|
||||
<div className="menu-toggle" onClick={onToggleFold} />
|
||||
</header>;
|
||||
}
|
12
components/mdx.tsx
Normal file
12
components/mdx.tsx
Normal file
|
@ -0,0 +1,12 @@
|
|||
/** <S $="span" _=".a.b.c" className="extra class-name">...</S> */
|
||||
export function S<E extends React.ElementType='span'>({
|
||||
$: TagName = 'span',
|
||||
_: modifier = '',
|
||||
className, children, ...props
|
||||
}: {
|
||||
$?: string,
|
||||
_?: string,
|
||||
} & React.ComponentProps<E>) {
|
||||
const cls = [className, ...(modifier.match(/\.(\w+)/g) ?? [])].map(s => s.slice(1)).join(' ');
|
||||
return <TagName className={cls} {...props}>{children}</TagName>;
|
||||
}
|
5
next-env.d.ts
vendored
Normal file
5
next-env.d.ts
vendored
Normal file
|
@ -0,0 +1,5 @@
|
|||
/// <reference types="next" />
|
||||
/// <reference types="next/image-types/global" />
|
||||
|
||||
// NOTE: This file should not be edited
|
||||
// see https://nextjs.org/docs/basic-features/typescript for more information.
|
30
next.config.mjs
Normal file
30
next.config.mjs
Normal file
|
@ -0,0 +1,30 @@
|
|||
import mdx from '@next/mdx';
|
||||
import remarkFrontmatter from 'remark-frontmatter';
|
||||
import remarkMdxFrontmatter from 'remark-mdx-frontmatter';
|
||||
import ExportHeadings from '@sup39/rehype-mdx-export-headings';
|
||||
import ComponentWrapper from '@sup39/rehype-mdx-component-wrapper';
|
||||
|
||||
const withMDX = mdx({
|
||||
extension: /\.mdx?$/,
|
||||
options: {
|
||||
remarkPlugins: [
|
||||
remarkFrontmatter,
|
||||
() => remarkMdxFrontmatter({name: 'meta'}),
|
||||
],
|
||||
rehypePlugins: [
|
||||
() => ExportHeadings({tags: ['h2'], name: 'headings'}),
|
||||
() => ComponentWrapper({props: ['headings', 'meta']}),
|
||||
],
|
||||
},
|
||||
});
|
||||
export default withMDX({
|
||||
pageExtensions: ['ts', 'tsx', 'js', 'jsx', 'md', 'mdx'],
|
||||
trailingSlash: true,
|
||||
webpack(config) {
|
||||
config.module.rules.push({
|
||||
test: /\.ya?ml$/,
|
||||
use: 'yaml-loader',
|
||||
});
|
||||
return config;
|
||||
},
|
||||
});
|
67
package.json
Normal file
67
package.json
Normal file
|
@ -0,0 +1,67 @@
|
|||
{
|
||||
"name": "supMDX",
|
||||
"version": "0.1.0",
|
||||
"main": "index.js",
|
||||
"license": "MIT",
|
||||
"scripts": {
|
||||
"dev": "next dev",
|
||||
"build": "next build",
|
||||
"start": "next start",
|
||||
"lint": "eslint --ext .js,.jsx,.ts,.tsx,.md,.mdx ."
|
||||
},
|
||||
"eslintConfig": {
|
||||
"extends": [
|
||||
"next/core-web-vitals",
|
||||
"@sup39/typescript"
|
||||
],
|
||||
"overrides": [
|
||||
{
|
||||
"files": [
|
||||
"*.ts",
|
||||
"*.tsx"
|
||||
],
|
||||
"rules": {
|
||||
"no-undef": "off"
|
||||
}
|
||||
},
|
||||
{
|
||||
"files": [
|
||||
"*.mdx"
|
||||
],
|
||||
"extends": [
|
||||
"plugin:mdx/recommended"
|
||||
],
|
||||
"rules": {
|
||||
"no-trailing-spaces": "off",
|
||||
"indent": "off"
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
"eslintIgnore": [
|
||||
"node_modules"
|
||||
],
|
||||
"dependencies": {
|
||||
"@mdx-js/loader": "^2.1.5",
|
||||
"@mdx-js/react": "^2.1.5",
|
||||
"@next/mdx": "^13.0.2",
|
||||
"@sup39/rehype-mdx-component-wrapper": "^0.1.0",
|
||||
"@sup39/rehype-mdx-export-headings": "^0.1.1",
|
||||
"next": "^13.0.2",
|
||||
"react": "^18.2.0",
|
||||
"react-dom": "^18.2.0",
|
||||
"remark-frontmatter": "^4.0.1",
|
||||
"remark-mdx-frontmatter": "^2.1.1",
|
||||
"sass": "^1.56.0",
|
||||
"yaml-loader": "^0.8.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@sup39/eslint-config-typescript": "^0.1.2",
|
||||
"@types/node": "18.11.9",
|
||||
"@types/react": "18.0.24",
|
||||
"eslint": "^8.26.0",
|
||||
"eslint-config-next": "^13.0.2",
|
||||
"eslint-plugin-mdx": "^2.0.5",
|
||||
"typescript": "4.8.4"
|
||||
}
|
||||
}
|
20
pages/_app.tsx
Normal file
20
pages/_app.tsx
Normal file
|
@ -0,0 +1,20 @@
|
|||
import React from 'react';
|
||||
import type {AppProps} from 'next/app';
|
||||
import {MDXProvider} from '@mdx-js/react';
|
||||
import {S} from '@/mdx';
|
||||
import '../styles/index.sass';
|
||||
|
||||
// add anchor to all headings having id
|
||||
const hx = ['h1', 'h2', 'h3', 'h4', 'h5', 'h6'] as const;
|
||||
const extendedHx = Object.fromEntries(hx.map(H => [H,
|
||||
({children, id, ...props}: React.ComponentProps<(typeof hx)[number]>) => <H id={id} {...props}>
|
||||
{id && <a className="anchor" />}
|
||||
{children}
|
||||
</H>,
|
||||
]));
|
||||
|
||||
export default function App({Component, pageProps, router: {pathname}}: AppProps) {
|
||||
return <MDXProvider components={{S, ...extendedHx}}>
|
||||
<Component data={{pathname}} {...pageProps} />
|
||||
</MDXProvider>;
|
||||
}
|
8
pages/index.mdx
Normal file
8
pages/index.mdx
Normal file
|
@ -0,0 +1,8 @@
|
|||
---
|
||||
title: supMDX
|
||||
description: You can write any meta data you want
|
||||
author: me
|
||||
---
|
||||
|
||||
## h2
|
||||
### h3
|
27
styles/a.sass
Normal file
27
styles/a.sass
Normal file
|
@ -0,0 +1,27 @@
|
|||
// SPDX-License-Identifier: MIT
|
||||
// Copyright (c) 2022 sup39
|
||||
|
||||
a, .link
|
||||
color: #72E5DB
|
||||
text-decoration: none
|
||||
cursor: pointer
|
||||
a:hover, .link:hover
|
||||
color: #72E5DB
|
||||
text-decoration: underline
|
||||
a:active, .link:active
|
||||
color: #A0E5DF
|
||||
text-decoration: underline
|
||||
|
||||
a.anchor
|
||||
float: left
|
||||
padding-right: 4px
|
||||
margin-left: -24px
|
||||
a.anchor:before
|
||||
content: ""
|
||||
background-image: url("https://cdn.sup39.dev/img/anchor.svg")
|
||||
width: 16px
|
||||
height: 16px
|
||||
vertical-align: middle
|
||||
margin: 0 0 4px 0
|
||||
display: inline-block
|
||||
visibility: hidden
|
23
styles/code.sass
Normal file
23
styles/code.sass
Normal file
|
@ -0,0 +1,23 @@
|
|||
// SPDX-License-Identifier: MIT
|
||||
// Copyright (c) 2022 sup39
|
||||
|
||||
code
|
||||
font-family: Menlo, Monaco, Consolas, "Courier New", "Hiragino Kaku Gothic ProN", monospace
|
||||
padding: 2px 4px
|
||||
margin: 0 1px 0 0
|
||||
font-size: 90%
|
||||
font-variant-numeric: slashed-zero
|
||||
background-color: #4a4a4a
|
||||
border-radius: 4px
|
||||
white-space: nowrap
|
||||
pre code
|
||||
white-space: break-spaces
|
||||
background: unset
|
||||
padding: 0
|
||||
margin: 0
|
||||
pre
|
||||
background: #333
|
||||
color: #fff
|
||||
border-radius: 4px
|
||||
overflow-x: auto
|
||||
padding: 0.6em
|
35
styles/heading.sass
Normal file
35
styles/heading.sass
Normal file
|
@ -0,0 +1,35 @@
|
|||
// SPDX-License-Identifier: MIT
|
||||
// Copyright (c) 2022 sup39
|
||||
|
||||
h1, h2, h3, h4, h5, h6
|
||||
margin: 0
|
||||
h1
|
||||
font-size: 2.5em
|
||||
font-weight: 600
|
||||
line-height: 1.3
|
||||
margin-bottom: 0.5em
|
||||
h2
|
||||
font-size: 1.8em
|
||||
font-weight: 500
|
||||
line-height: 1.3
|
||||
padding-bottom: 0.1em
|
||||
border-bottom: 1px solid var(--bd)
|
||||
margin-top: 1.0em
|
||||
margin-bottom: 0.25em
|
||||
h3
|
||||
font-size: 1.5em
|
||||
font-weight: bold
|
||||
line-height: 1.6
|
||||
margin-top: 0.5em
|
||||
h4
|
||||
font-size: 1.2em
|
||||
font-weight: 600
|
||||
margin-top: 1em
|
||||
h5
|
||||
font-size: 1.1em
|
||||
font-weight: 500
|
||||
margin-top: 1em
|
||||
h6
|
||||
font-size: 1em
|
||||
font-weight: 400
|
||||
margin-top: 1em
|
11
styles/index.sass
Normal file
11
styles/index.sass
Normal file
|
@ -0,0 +1,11 @@
|
|||
@import './vars.sass'
|
||||
@import './nav.sass'
|
||||
@import './menu-toggle.sass'
|
||||
@import './heading.sass'
|
||||
@import './span.sass'
|
||||
@import './a.sass'
|
||||
@import './code.sass'
|
||||
@import './misc.sass'
|
||||
|
||||
@media only screen and (min-width: 768px)
|
||||
@import './nav.pc.sass'
|
37
styles/menu-toggle.sass
Normal file
37
styles/menu-toggle.sass
Normal file
|
@ -0,0 +1,37 @@
|
|||
// SPDX-License-Identifier: MIT
|
||||
// Copyright (c) 2022 sup39
|
||||
|
||||
.menu-toggle
|
||||
width: 20px
|
||||
height: 20px
|
||||
display: block
|
||||
cursor: pointer
|
||||
.menu-toggle:before
|
||||
top: 30%
|
||||
.menu-toggle:after
|
||||
top: 70%
|
||||
|
||||
.menu-toggle:before,
|
||||
.menu-toggle:after
|
||||
position: absolute
|
||||
content: ""
|
||||
width: 100%
|
||||
height: 2px
|
||||
transform: translate(0, -50%)
|
||||
background: var(--fg)
|
||||
transition: all .25s ease-out
|
||||
|
||||
.open
|
||||
.menu-toggle:before
|
||||
transform: translate(0, -50%) rotate(135deg)
|
||||
.menu-toggle:after
|
||||
transform: translate(0, -50%) rotate(-135deg)
|
||||
.menu-toggle:before,
|
||||
.menu-toggle:after
|
||||
top: 50%
|
||||
left: -10%
|
||||
width: 120%
|
||||
|
||||
@media only screen and (min-width: 768px)
|
||||
.menu-toggle
|
||||
display: none
|
261
styles/misc.sass
Normal file
261
styles/misc.sass
Normal file
|
@ -0,0 +1,261 @@
|
|||
// SPDX-License-Identifier: MIT
|
||||
// Copyright (c) 2022 sup39
|
||||
|
||||
html
|
||||
font-size: 16px
|
||||
body
|
||||
color: var(--fg)
|
||||
background: var(--bg)
|
||||
line-height: 1.6
|
||||
font-weight: 300
|
||||
font-family: -apple-system, BlinkMacSystemFont, "Helvetica Neue", Helvetica, Arial, Verdana, "Hiragino Kaku Gothic ProN", "Hiragino Sans", Meiryo, sans-serif
|
||||
font-variant-numeric: tabular-nums
|
||||
margin: 0
|
||||
*
|
||||
box-sizing: border-box
|
||||
|
||||
/**** font ****/
|
||||
html:lang(zh-TW) body
|
||||
font-family: "SF Pro TC","SF Pro Text","SF Pro Icons","Helvetica Neue","Helvetica","Arial",sans-serif
|
||||
|
||||
/**** image ****/
|
||||
article img
|
||||
max-width: 100%
|
||||
height: auto
|
||||
margin: 1em 0
|
||||
|
||||
#icon-link
|
||||
text-decoration: none
|
||||
> *
|
||||
float: left
|
||||
.icon
|
||||
width: 72px
|
||||
height: 72px
|
||||
.icon-text
|
||||
height: 72px
|
||||
font-size: 27px
|
||||
font-weight: 600
|
||||
line-height: 1.2
|
||||
padding-left: 16px
|
||||
display: flex
|
||||
flex-direction: column
|
||||
align-items: center
|
||||
justify-content: center
|
||||
text-align: center
|
||||
.pink
|
||||
font-size: 24px
|
||||
#icon-link:link
|
||||
color: unset
|
||||
|
||||
/**** main ****/
|
||||
main
|
||||
padding: 24px
|
||||
article
|
||||
font-weight: 300
|
||||
h3
|
||||
+ table, + h4
|
||||
margin-top: 0.5em
|
||||
h4 + table
|
||||
margin-top: 0.5em
|
||||
table
|
||||
margin-block-start: 1em
|
||||
margin-block-end: 1em
|
||||
table,
|
||||
table th,
|
||||
table td
|
||||
border: solid 1px var(--bd)
|
||||
border-collapse: collapse
|
||||
padding: 4px
|
||||
> ul
|
||||
padding-inline-start: 2em
|
||||
margin-block-start: 0.5em
|
||||
margin-block-end: 1em
|
||||
ul ul
|
||||
padding-inline-start: 1.25em
|
||||
> h2 + ul
|
||||
padding-inline-start: 1.5em
|
||||
|
||||
footer
|
||||
background: #18181e
|
||||
background: #222
|
||||
font-size: 0.75em
|
||||
line-height: 1em
|
||||
padding: 1.5em 2em
|
||||
margin-top: 1em
|
||||
|
||||
p + *.compact
|
||||
margin-block-start: -1em
|
||||
*.compact
|
||||
margin-block-start: 0em
|
||||
|
||||
/**** table ****/
|
||||
.tac
|
||||
text-align: center
|
||||
.tar
|
||||
text-align: right
|
||||
table.tb-r tbody
|
||||
text-align: right
|
||||
|
||||
/**** form ****/
|
||||
form > div
|
||||
display: flex
|
||||
align-items: center
|
||||
> *
|
||||
margin: 3px
|
||||
|
||||
/**** input ****/
|
||||
input, textarea
|
||||
color: var(--fg)
|
||||
background: #222
|
||||
border: var(--bd) solid 1px
|
||||
font-size: 1em
|
||||
input[invalid]
|
||||
background: #600
|
||||
input[type="number"]
|
||||
text-align: right
|
||||
|
||||
/**** button ****/
|
||||
button,
|
||||
input[type="button"],
|
||||
input[type="submit"],
|
||||
input[type="reset"]
|
||||
color: var(--fg)
|
||||
background: #2a8
|
||||
border: #2a8 1px solid
|
||||
border-radius: 4px
|
||||
font-size: 14px
|
||||
cursor: pointer
|
||||
button:hover,
|
||||
input[type="button"]:hover,
|
||||
input[type="submit"]:hover,
|
||||
input[type="reset"]:hover
|
||||
border: #197 1px solid
|
||||
background: #197
|
||||
button:active,
|
||||
input[type="button"]:active,
|
||||
input[type="submit"]:active,
|
||||
input[type="reset"]:active
|
||||
border: #075 1px solid
|
||||
background: #075
|
||||
|
||||
button:disabled
|
||||
background: #444
|
||||
border: #555 1px solid
|
||||
cursor: not-allowed
|
||||
button[variant="warning"]
|
||||
background: #c87603
|
||||
border: #c87603 1px solid
|
||||
button[variant="warning"]:enabled:hover
|
||||
background: #b60
|
||||
border: #b60 1px solid
|
||||
button[variant="warning"]:enabled:active
|
||||
background: #a50
|
||||
border: #b60 1px solid
|
||||
button[variant="danger"]:enabled
|
||||
background: #c32
|
||||
border: #c32 1px solid
|
||||
button[variant="danger"]:enabled:hover
|
||||
background: #b21
|
||||
border: #b21 1px solid
|
||||
button[variant="danger"]:enabled:active
|
||||
background: #a10
|
||||
border: #b21 1px solid
|
||||
button.sm
|
||||
font-size: 12px
|
||||
|
||||
/**** div.bt ****/
|
||||
div.bt-plus, div.bt-minus
|
||||
position: relative
|
||||
width: 1.2em
|
||||
height: 1.2em
|
||||
border: #2ee5b8 1px solid
|
||||
border-radius: 4px
|
||||
cursor: pointer
|
||||
user-select: none
|
||||
-webkit-user-select: none
|
||||
-moz-user-select: none
|
||||
-khtml-user-select: none
|
||||
-ms-user-select: none
|
||||
div.bt-plus:before,
|
||||
div.bt-plus:after,
|
||||
div.bt-minus:before
|
||||
content: ""
|
||||
position: absolute
|
||||
top: 50%
|
||||
left: 50%
|
||||
transform: translate(-50%, -50%)
|
||||
width: 50%
|
||||
height: 1px
|
||||
background: var(--fg)
|
||||
div.bt-plus:after
|
||||
width: 1px
|
||||
height: 50%
|
||||
|
||||
/**** variant ****/
|
||||
span.danger
|
||||
color: #f66
|
||||
button.danger
|
||||
background: #a33
|
||||
|
||||
/**** details ****/
|
||||
details
|
||||
border: 1px solid
|
||||
padding: 0.5em 1em
|
||||
margin-block-start: 0.5em
|
||||
margin-block-end: 0.5em
|
||||
> summary
|
||||
padding: 4px 0.5em
|
||||
margin: -0.5em -1em -0.5em
|
||||
details[open]
|
||||
padding-bottom: 0
|
||||
> summary
|
||||
border-bottom: 1px solid
|
||||
margin: -0.5em -1em 0
|
||||
> ul, > ol
|
||||
padding-inline-start: 1.5em
|
||||
|
||||
/**** misc ****/
|
||||
video
|
||||
max-width: 100%
|
||||
.noselect
|
||||
-webkit-touch-callout: none // iOS Safari
|
||||
-webkit-user-select: none // Safari
|
||||
-khtml-user-select: none // Konqueror HTML
|
||||
-moz-user-select: none // Old versions of Firefox
|
||||
-ms-user-select: none // Internet Explorer/Edge
|
||||
user-select: none // Non-prefixed version, currently supported by Chrome, Edge, Opera and Firefox
|
||||
|
||||
/**** hide ****/
|
||||
div.hide-ctn
|
||||
position: relative
|
||||
color: #fff
|
||||
margin-block-start: -1em
|
||||
margin-block-end: 2em
|
||||
z-index:-1
|
||||
*
|
||||
position: absolute
|
||||
font-size: 0.25em
|
||||
color: #0000
|
||||
|
||||
/**** highlight ****/
|
||||
table.code-lnum
|
||||
padding: 0
|
||||
margin: 0
|
||||
width: 100%
|
||||
border: none
|
||||
td
|
||||
border: none
|
||||
td:first-child
|
||||
padding-left: 0.5em
|
||||
padding-right: 1em
|
||||
text-align: right
|
||||
td:last-child
|
||||
width: 100%
|
||||
white-space: pre
|
||||
.hljs-comment
|
||||
color: #7d6
|
||||
.hljs-keyword
|
||||
color: #ffa
|
||||
font-weight: bold
|
||||
.hljs-number, .hljs-string
|
||||
color: #ffb7e7
|
32
styles/nav.pc.sass
Normal file
32
styles/nav.pc.sass
Normal file
|
@ -0,0 +1,32 @@
|
|||
// SPDX-License-Identifier: MIT
|
||||
// Copyright (c) 2022 sup39
|
||||
|
||||
nav
|
||||
width: var(--nav-width)
|
||||
overflow: auto
|
||||
position: fixed
|
||||
left: 0
|
||||
top: 0
|
||||
bottom: 0
|
||||
border-bottom: none
|
||||
border-right: 1px solid var(--bd)
|
||||
padding: 1em 1em
|
||||
header
|
||||
margin-bottom: 1em
|
||||
.menu-toggle
|
||||
display: none
|
||||
.nav-root
|
||||
height: auto
|
||||
opacity: 1
|
||||
visibility: visible
|
||||
|
||||
main, footer
|
||||
margin-left: var(--nav-width)
|
||||
main
|
||||
padding: 24px 32px
|
||||
|
||||
*:hover > a.anchor:before
|
||||
visibility: visible
|
||||
|
||||
.mobile-only
|
||||
display: none
|
107
styles/nav.sass
Normal file
107
styles/nav.sass
Normal file
|
@ -0,0 +1,107 @@
|
|||
// SPDX-License-Identifier: MIT
|
||||
// Copyright (c) 2022 sup39
|
||||
|
||||
$yellow: #ff9
|
||||
$bd: var(--bd)
|
||||
|
||||
/** mobile */
|
||||
nav
|
||||
width: 100%
|
||||
background: #28282e
|
||||
border-bottom: 1px solid var(--bd)
|
||||
border-right: none
|
||||
padding: 1.25em 1.25em
|
||||
|
||||
header
|
||||
display: flex
|
||||
justify-content: center
|
||||
position: relative
|
||||
|
||||
.menu-toggle
|
||||
position: absolute
|
||||
top: 50%
|
||||
right: 1em
|
||||
transform: translate(-50%, -50%)
|
||||
|
||||
.nav-root
|
||||
height: 0
|
||||
// opacity: 0
|
||||
visibility: hidden
|
||||
|
||||
*
|
||||
padding: 0
|
||||
margin: 0
|
||||
|
||||
a
|
||||
display: block
|
||||
color: #eee
|
||||
|
||||
.nav-dir-child
|
||||
padding-left: 1em
|
||||
|
||||
.nav-here, .nav-here a,
|
||||
.nav-here + ul, .nav-here + ul a
|
||||
color: $yellow
|
||||
ul
|
||||
list-style-type: square
|
||||
margin-block-start: 0
|
||||
margin-block-end: 0
|
||||
padding-inline-start: 24px
|
||||
|
||||
.nav-root
|
||||
> div
|
||||
border-top: 1px solid $bd
|
||||
padding: 0.5em 0.5em
|
||||
|
||||
/** heading list */
|
||||
> ul
|
||||
border-top: 1px solid $bd
|
||||
padding-top: 0.75em
|
||||
// margin-bottom: 0.5em
|
||||
|
||||
|
||||
/** here */
|
||||
.nav-here
|
||||
font-weight: bold
|
||||
|
||||
|
||||
/** container of entry */
|
||||
.nav-entry
|
||||
display: flex
|
||||
align-items: center
|
||||
|
||||
> a
|
||||
flex-grow: 1
|
||||
|
||||
> svg
|
||||
stroke: #9ff
|
||||
fill: none
|
||||
stroke-width: 1
|
||||
stroke-linecap: round
|
||||
stroke-linejoin: round
|
||||
width: 1.2em
|
||||
height: 1.2em
|
||||
cursor: pointer
|
||||
border-radius: 50%
|
||||
> svg:hover
|
||||
background: #18181e
|
||||
|
||||
|
||||
/** folder hide */
|
||||
.nav-dir-child
|
||||
display: none
|
||||
|
||||
.nav-fold-open > .nav-dir-child
|
||||
display: block
|
||||
|
||||
.nav-fold-open > .nav-entry > svg
|
||||
rotate: 180deg
|
||||
|
||||
nav.open
|
||||
header
|
||||
margin-bottom: 1.25em
|
||||
.nav-root
|
||||
height: auto
|
||||
opacity: 1
|
||||
visibility: visible
|
||||
// transition: opacity 0.4s ease-in-out
|
29
styles/span.sass
Normal file
29
styles/span.sass
Normal file
|
@ -0,0 +1,29 @@
|
|||
// SPDX-License-Identifier: MIT
|
||||
// Copyright (c) 2022 sup39
|
||||
|
||||
.mint
|
||||
color: var(--mint)
|
||||
.pink
|
||||
color: var(--pink)
|
||||
.kw
|
||||
color: #9ff
|
||||
font-weight: bold
|
||||
.mk
|
||||
color: #ff99e5
|
||||
font-weight: bold
|
||||
.y
|
||||
color: #ff7
|
||||
font-weight: bold
|
||||
.note
|
||||
color: #777
|
||||
.arg
|
||||
color: #da8cff
|
||||
font-weight: bold
|
||||
.u
|
||||
text-decoration: underline
|
||||
font-weight: bold
|
||||
.mono
|
||||
font-family: monospace
|
||||
span[title]
|
||||
cursor: help
|
||||
text-decoration: dotted underline
|
10
styles/vars.sass
Normal file
10
styles/vars.sass
Normal file
|
@ -0,0 +1,10 @@
|
|||
// SPDX-License-Identifier: MIT
|
||||
// Copyright (c) 2022 sup39
|
||||
|
||||
:root
|
||||
--bg: #222228
|
||||
--fg: #eee
|
||||
--bd: #777
|
||||
--mint: #2ee5b8
|
||||
--pink: #e58acf
|
||||
--nav-width: 256px
|
16
supMDX-env.d.ts
vendored
Normal file
16
supMDX-env.d.ts
vendored
Normal file
|
@ -0,0 +1,16 @@
|
|||
declare module '#config' {
|
||||
import type {NavEntry} from '@/Nav';
|
||||
type Config = {
|
||||
startYear: number
|
||||
siteAuthor?: string
|
||||
metaFields?: {label: string, prop: string}[]
|
||||
nav: NavEntry[]
|
||||
};
|
||||
const config: Config;
|
||||
export default config;
|
||||
}
|
||||
|
||||
declare module '*.yaml' {
|
||||
const data: any;
|
||||
export default data;
|
||||
}
|
7
supMDX.yml
Normal file
7
supMDX.yml
Normal file
|
@ -0,0 +1,7 @@
|
|||
startYear: 2022
|
||||
siteAuthor: <Edit author name in supMDX.yml>
|
||||
metaFields:
|
||||
- {prop: author, label: 'Author: '}
|
||||
- {prop: createdAt, label: 'Created at '}
|
||||
- {prop: updatedAt, label: 'Updated at '}
|
||||
nav: []
|
37
tsconfig.json
Normal file
37
tsconfig.json
Normal file
|
@ -0,0 +1,37 @@
|
|||
{
|
||||
"compilerOptions": {
|
||||
"baseUrl": ".",
|
||||
"paths": {
|
||||
"@/*": ["components/*"],
|
||||
"#/*": ["pages/*"],
|
||||
"#config": ["supMDX.yml"],
|
||||
"%/*": ["utils/*"]
|
||||
},
|
||||
"target": "es5",
|
||||
"lib": [
|
||||
"dom",
|
||||
"dom.iterable",
|
||||
"esnext"
|
||||
],
|
||||
"allowJs": true,
|
||||
"skipLibCheck": true,
|
||||
"strict": true,
|
||||
"forceConsistentCasingInFileNames": true,
|
||||
"noEmit": true,
|
||||
"incremental": true,
|
||||
"esModuleInterop": true,
|
||||
"module": "esnext",
|
||||
"moduleResolution": "node",
|
||||
"resolveJsonModule": true,
|
||||
"isolatedModules": true,
|
||||
"jsx": "preserve"
|
||||
},
|
||||
"include": [
|
||||
"next-env.d.ts",
|
||||
"**/*.ts",
|
||||
"**/*.tsx"
|
||||
],
|
||||
"exclude": [
|
||||
"node_modules"
|
||||
]
|
||||
}
|
Loading…
Reference in a new issue