wip: default theme

pull/20/head
Evan You 5 years ago
parent 382e1b6514
commit 1be0a0b0a1

@ -1,4 +1,4 @@
import { reactive, inject, nextTick, markRaw } from 'vue'
import { reactive, inject, markRaw } from 'vue'
import type { Component, InjectionKey } from 'vue'
export interface Route {
@ -13,6 +13,10 @@ export interface Router {
export const RouterSymbol: InjectionKey<Router> = Symbol()
// we are just using URL to parse the pathname and hash - the base doesn't
// matter and is only passed to support same-host hrefs.
const fakeHost = `http://a.com`
const getDefaultRoute = (): Route => ({
path: '/',
contentComponent: null
@ -27,6 +31,12 @@ export function createRouter(
function go(href?: string) {
href = href || (inBrowser ? location.href : '/')
// ensure correct deep link so page refresh lands on correct files.
const url = new URL(href, fakeHost)
if (!url.pathname.endsWith('/') && !url.pathname.endsWith('.html')) {
url.pathname += '.html'
href = url.href
}
if (inBrowser) {
// save scroll position before changing url
history.replaceState({ scrollPosition: window.scrollY }, document.title)
@ -36,11 +46,8 @@ export function createRouter(
}
async function loadPage(href: string, scrollPosition = 0) {
// we are just using URL to parse the pathname and hash - the base doesn't
// matter and is only passed to support same-host hrefs.
const targetLoc = new URL(href, `http://vuejs.org`)
const targetLoc = new URL(href, fakeHost)
const pendingPath = (route.path = targetLoc.pathname)
try {
let comp = loadComponent(route)
// only await if it returns a Promise - this allows sync resolution
@ -54,19 +61,17 @@ export function createRouter(
}
route.contentComponent = markRaw(comp)
if (inBrowser) {
await nextTick()
if (targetLoc.hash && !scrollPosition) {
const target = document.querySelector(targetLoc.hash) as HTMLElement
if (target) {
scrollPosition = target.offsetTop
setTimeout(() => {
if (targetLoc.hash && !scrollPosition) {
const target = document.querySelector(
targetLoc.hash
) as HTMLElement
if (target) {
scrollTo(target, targetLoc.hash, false)
return
}
}
}
window.scrollTo({
left: 0,
top: scrollPosition,
behavior: 'auto'
window.scrollTo(0, scrollPosition)
})
}
}
@ -99,14 +104,9 @@ export function createRouter(
e.preventDefault()
if (pathname === currentUrl.pathname) {
// smooth scroll bewteen hash anchors in the same page
if (hash !== currentUrl.hash) {
// calculate the offset based on app's offset
const pageOffset = document.getElementById('app')!.offsetTop
window.scrollTo({
left: 0,
top: link.offsetTop - pageOffset - 15,
behavior: 'smooth'
})
if (hash && hash !== currentUrl.hash) {
history.pushState(null, '', hash)
scrollTo(link, hash)
}
} else {
go(href)
@ -120,6 +120,10 @@ export function createRouter(
window.addEventListener('popstate', (e) => {
loadPage(location.href, (e.state && e.state.scrollPosition) || 0)
})
window.addEventListener('hashchange', (e) => {
e.preventDefault()
})
}
return {
@ -140,3 +144,26 @@ export function useRouter(): Router {
export function useRoute(): Route {
return useRouter().route
}
function scrollTo(el: HTMLElement, hash: string, smooth = true) {
const pageOffset = document.getElementById('app')!.offsetTop
const target = el.classList.contains('.header-anchor')
? el
: document.querySelector(hash)
if (target) {
const targetTop = (target as HTMLElement).offsetTop - pageOffset - 15
const currentTop = window.scrollY
const distance = Math.abs(targetTop - currentTop)
// only smooth scroll if distance is smaller than 1.5 x
// screen height.
if (!smooth || distance > window.innerHeight * 1.5) {
window.scrollTo(0, targetTop)
} else {
window.scrollTo({
left: 0,
top: targetTop,
behavior: 'smooth'
})
}
}
}

@ -27,6 +27,7 @@
</template>
<script>
// TODO dropdowns
import { computed } from 'vue'
import { useSiteData, useRoute } from 'vitepress'
import { withBase } from '../utils'
@ -81,7 +82,7 @@ export default {
.nav-link {
color: var(--text-color);
margin-left: 1.5rem;
font-weight: 500;
font-weight: 600;
display: inline-block;
height: 1.75rem;
line-height: 1.75rem;

@ -8,7 +8,7 @@
.content {
max-width: 46rem;
margin: 0px auto;
padding: 1rem 1.5rem;
padding: 1rem 2rem;
}
.content a {
@ -18,4 +18,8 @@
.content a:hover {
text-decoration: underline;
}
.content img {
max-width: 100%;
}
</style>

@ -1,5 +1,5 @@
<template>
<ul>
<ul class="sidebar">
<li>sidebar</li>
</ul>
</template>

@ -1,4 +1,7 @@
import './layout.css'
import './styles/vars.css'
import './styles/layout.css'
import './styles/code.css'
import Layout from './Layout.vue'
import NotFound from './NotFound.vue'
import { Theme } from '../app/theme'

@ -0,0 +1,270 @@
code {
font-family: source-code-pro, Menlo, Monaco, Consolas, Courier New, monospace;
font-size: 0.9em;
color: var(--text-color-light);
background-color: rgba(27, 31, 35, 0.05);
padding: 0.25rem 0.5rem;
border-radius: 3px;
margin: 0;
}
code .token.deleted {
color: #ec5975;
}
code .token.inserted {
color: var(--accent-color);
}
div[class*='language-'] {
line-height: 1.5;
padding: 0.5rem 1.5rem;
background-color: var(--code-bg-color);
border-radius: 6px;
overflow-x: auto;
position: relative;
}
[class*='language-'] pre {
background: transparent;
position: relative;
z-index: 1;
}
[class*='language-'] code {
color: #eee;
padding: 0;
}
[class*='language-'] code,
[class*='language-'] pre {
text-align: left;
white-space: pre;
word-spacing: normal;
word-break: normal;
word-wrap: normal;
line-height: 1.5;
-moz-tab-size: 4;
-o-tab-size: 4;
tab-size: 4;
-webkit-hyphens: none;
-moz-hyphens: none;
-ms-hyphens: none;
hyphens: none;
}
/* Line highlighting */
.highlight-lines {
font-size: 0.9em;
user-select: none;
padding-top: 1.3rem;
position: absolute;
top: 0;
left: 0;
width: 100%;
line-height: 1.5;
}
.highlight-lines .highlighted {
background-color: rgba(0, 0, 0, 66%);
}
/* Line numbers mode */
div[class*='language-'].line-numbers-mode {
padding-left: 5rem;
}
.line-numbers-wrapper {
position: absolute;
top: 0;
left: 0;
width: 3.5rem;
text-align: center;
color: #888;
line-height: 1.5;
font-size: 0.9em;
padding: 1.3rem 0;
border-right: 1px solid rgba(0,0,0,50%);
z-index: 4;
}
/* Language marker */
[class*='language-']:before {
position: absolute;
z-index: 3;
top: 0.6em;
right: 1em;
font-size: 0.8rem;
color: #888;
}
[class~='language-html']:before,
[class~='language-markup']:before {
content: 'html';
}
[class~='language-md']:before,
[class~='language-markdown']:before {
content: 'md';
}
[class~='language-css']:before {
content: 'css';
}
[class~='language-sass']:before {
content: 'sass';
}
[class~='language-scss']:before {
content: 'scss';
}
[class~='language-less']:before {
content: 'less';
}
[class~='language-stylus']:before {
content: 'styl';
}
[class~='language-js']:before,
[class~='language-typescript']:before {
content: 'js';
}
[class~='language-ts']:before,
[class~='language-typescript']:before {
content: 'ts';
}
[class~='language-json']:before {
content: 'json';
}
[class~='language-rb']:before,
[class~='language-ruby']:before {
content: 'rb';
}
[class~='language-py']:before,
[class~='language-python']:before {
content: 'py';
}
[class~='language-sh']:before,
[class~='language-bash']:before {
content: 'sh';
}
[class~='language-php']:before {
content: 'php';
}
[class~='language-go']:before {
content: 'go';
}
[class~='language-rust']:before {
content: 'rust';
}
[class~='language-java']:before {
content: 'java';
}
[class~='language-c']:before {
content: 'c';
}
[class~='language-yaml']:before {
content: 'yaml';
}
[class~='language-dockerfile']:before {
content: 'dockerfile';
}
/**
* prism.js tomorrow night eighties for JavaScript, CoffeeScript, CSS and HTML
* Based on https://github.com/chriskempson/tomorrow-theme
* @author Rose Pritchard
*/
.token.comment,
.token.block-comment,
.token.prolog,
.token.doctype,
.token.cdata {
color: #999;
}
.token.punctuation {
color: #ccc;
}
.token.tag,
.token.attr-name,
.token.namespace,
.token.deleted {
color: #e2777a;
}
.token.function-name {
color: #6196cc;
}
.token.boolean,
.token.number,
.token.function {
color: #f08d49;
}
.token.property,
.token.class-name,
.token.constant,
.token.symbol {
color: #f8c555;
}
.token.selector,
.token.important,
.token.atrule,
.token.keyword,
.token.builtin {
color: #cc99cd;
}
.token.string,
.token.char,
.token.attr-value,
.token.regex,
.token.variable {
color: #7ec699;
}
.token.operator,
.token.entity,
.token.url {
color: #67cdcc;
}
.token.important,
.token.bold {
font-weight: bold;
}
.token.italic {
font-style: italic;
}
.token.entity {
cursor: help;
}
.token.inserted {
color: green;
}

@ -9,11 +9,8 @@ body {
.theme {
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Helvetica, Arial,
sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol';
--border-color: rgb(226, 232, 240);
--header-height: 4rem;
--sidebar-width: 18rem;
--text-color: #2c3e50;
--accent-color: #3eaf7c;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
color: var(--text-color);
}
@ -54,6 +51,65 @@ a {
text-decoration: none;
}
h1, h2, h3, h4, h5, h6, strong, b {
h1,
h2,
h3,
h4,
h5,
h6,
strong,
b {
font-weight: 600;
line-height: 1.6;
}
h1 {
font-size: 2.2rem;
}
h2 {
font-size: 1.65rem;
padding-bottom: .3rem;
border-bottom: 1px solid var(--border-color);
}
h3 {
font-size: 1.35rem;
}
h4 {
font-size: 1.15rem;
}
a.header-anchor {
font-size: 0.85em;
float: left;
margin-left: -0.87em;
padding-right: 0.23em;
margin-top: 0.125em;
opacity: 0;
}
a.header-anchor:hover {
text-decoration: none;
}
h1:hover .header-anchor,
h2:hover .header-anchor,
h3:hover .header-anchor,
h4:hover .header-anchor,
h5:hover .header-anchor,
h6:hover .header-anchor {
opacity: 1;
}
p,
ol,
ul {
line-height: 1.6;
}
ul,
ol {
padding-left: 1.25em;
}

@ -0,0 +1,9 @@
.theme {
--border-color: rgb(226, 232, 240);
--header-height: 4rem;
--sidebar-width: 18rem;
--text-color: #2c3e50;
--text-color-light: #476582;
--code-bg-color: #282c34;
--accent-color: #3eaf7c;
}

@ -10,7 +10,7 @@ function wrap(code: string, lang: string): string {
if (lang === 'text') {
code = escapeHtml(code)
}
return `<pre v-pre class="language-${lang}"><code>${code}</code></pre>`
return `<pre v-pre><code>${code}</code></pre>`
}
export const highlight = (str: string, lang: string) => {

@ -20,8 +20,8 @@ export const lineNumberPlugin = (md: MarkdownIt) => {
const lineNumbersWrapperCode = `<div class="line-numbers-wrapper">${lineNumbersCode}</div>`
const finalCode = rawCode
.replace('<!--beforeend-->', `${lineNumbersWrapperCode}<!--beforeend-->`)
.replace('extra-class', 'line-numbers-mode')
.replace(/<\/div>$/, `${lineNumbersWrapperCode}</div>`)
.replace(/"(language-\w+)"/, '"$1 line-numbers-mode"')
return finalCode
}

@ -15,9 +15,6 @@ export const preWrapperPlugin = (md: MarkdownIt) => {
const [tokens, idx] = args
const token = tokens[idx]
const rawCode = fence(...args)
return (
`<!--beforebegin--><div class="language-${token.info.trim()} extra-class">` +
`<!--afterbegin-->${rawCode}<!--beforeend--></div><!--afterend-->`
)
return `<div class="language-${token.info.trim()}">${rawCode}</div>`
}
}

Loading…
Cancel
Save