feat: add layout option and home hero section

pull/632/head
Kia King Ishii 3 years ago
parent 25296fa852
commit 69c44aa607

@ -18,7 +18,7 @@ export default defineConfig({
sidebar: { sidebar: {
'/guide/': getGuideSidebar(), '/guide/': getGuideSidebar(),
'/config/': getConfigSidebar(), '/config/': getConfigSidebar(),
'/': getGuideSidebar() // '/': getGuideSidebar()
}, },
editLink: { editLink: {
@ -54,8 +54,10 @@ function getConfigSidebar() {
{ {
text: 'Config', text: 'Config',
items: [ items: [
{ text: 'Introduction', link: '/config/introduction' },
{ text: 'App Configs', link: '/config/app-configs' }, { text: 'App Configs', link: '/config/app-configs' },
{ text: 'Theme Configs', link: '/config/theme-configs' } { text: 'Theme Configs', link: '/config/theme-configs' },
{ text: 'Frontmatter Configs', link: '/config/frontmatter-configs' }
] ]
} }
] ]

@ -0,0 +1,89 @@
# Frontmatter Configs
Frontmatter enables page based configuration. On every markdown, youre free to add Any settings to override any global app or theme configs. Also, there are configs which you can only define in Frontmatter.
```yaml
---
title: Docs with VitePress
editLink: true
---
```
You may access frontmatter by `$frontmatter` helper inside any markdown file.
```md
{{ $frontmatter.title }}
```
## title
- Type: `string`
Title for the page. It's same as [config.title](../config/app-configs#title), and it overrides the app config.
```yaml
---
title: VitePress
---
```
## description
- Type: `string`
Title for the page. It's same as [config.description](../config/app-configs#description), and it overrides the app config.
```yaml
---
description: VitePress
---
```
## layout
- Type: `doc | home | page`
- Default: `doc`
Determines the layout of the page.
- `doc` - It applies default documentation styles to the markdown content.
- `home` - Special layout for "Home Page". You may add extra options such as `hero` and `features` to rappidly create beautiful landing page.
- `page` - Behave similar to `doc` but it aplies no styles to the content. Useful when you want to create a fully custom page.
```yaml
---
type: doc
---
```
## hero
- Type: `Hero`
This option only take effect when `layout` is set to `home`.
It defines contents of home hero section.
```yaml
---
layout: home
hero:
name: VuePress
text: Vite & Vue powered static site generator.
tagline: Lorem ipsum...
---
```
```ts
interface Hero {
// The string shown top of `text`. Best used for product name.
name: string
// The main text for the hero section. This will be defined as `h1`.
text: string
// Tagline displayed below `text`.
tagline: string
}
```

@ -0,0 +1,3 @@
# Introduction
Here we plan to describe all about configurations. What the difference between app config and theme config, how to access them within markdown or in Vue Component and such.

@ -1,3 +1,8 @@
# Home Page ---
layout: home
Coming soon... hero:
name: VuePress
text: Vite & Vue powered static site generator.
tagline: Simple, minimal, yet powerful as lightning. Meet the modern SSG framework you've always wanted.
---

@ -1,10 +1,13 @@
<script setup lang="ts"> <script setup lang="ts">
import { useRoute } from 'vitepress' import { useRoute, useData } from 'vitepress'
import { useSidebar } from '../composables/sidebar' import { useSidebar } from '../composables/sidebar'
import NotFound from '../NotFound.vue' import NotFound from '../NotFound.vue'
import VPPage from './VPPage.vue'
import VPHome from './VPHome.vue'
import VPContentDoc from './VPContentDoc.vue' import VPContentDoc from './VPContentDoc.vue'
const route = useRoute() const route = useRoute()
const { frontmatter } = useData()
const { hasSidebar } = useSidebar() const { hasSidebar } = useSidebar()
</script> </script>
@ -15,11 +18,18 @@ const { hasSidebar } = useSidebar()
:class="{ 'has-sidebar': hasSidebar }" :class="{ 'has-sidebar': hasSidebar }"
> >
<NotFound v-if="route.component === NotFound" /> <NotFound v-if="route.component === NotFound" />
<VPPage v-else-if="frontmatter.layout === 'page'" />
<VPHome v-else-if="frontmatter.layout === 'home'" />
<VPContentDoc v-else :class="{ 'has-sidebar': hasSidebar }" /> <VPContentDoc v-else :class="{ 'has-sidebar': hasSidebar }" />
</div> </div>
</template> </template>
<style scoped> <style scoped>
.VPContent {
margin: 0 auto;
max-width: var(--vp-layout-max-width);
}
@media (max-width: 768px) { @media (max-width: 768px) {
.VPContent { .VPContent {
overflow-x: hidden; overflow-x: hidden;
@ -28,16 +38,19 @@ const { hasSidebar } = useSidebar()
@media (min-width: 960px) { @media (min-width: 960px) {
.VPContent { .VPContent {
padding-top: var(--vp-nav-height-desktop); padding-top: var(--vp-nav-height);
} }
.VPContent.has-sidebar { .VPContent.has-sidebar {
margin: 0;
padding-left: var(--vp-sidebar-width); padding-left: var(--vp-sidebar-width);
max-width: 100%;
} }
} }
@media (min-width: 1440px) { @media (min-width: 1440px) {
.VPContent.has-sidebar { .VPContent.has-sidebar {
padding-right: calc((100vw - var(--vp-layout-max-width)) / 2);
padding-left: calc((100vw - var(--vp-layout-max-width)) / 2 + var(--vp-sidebar-width) - 32px); padding-left: calc((100vw - var(--vp-layout-max-width)) / 2 + var(--vp-sidebar-width) - 32px);
} }
} }

@ -1,8 +1,8 @@
<script setup lang="ts"> <script setup lang="ts">
import { computed } from 'vue' import { computed } from 'vue'
import { useData } from 'vitepress' import { useData } from 'vitepress'
import VPContentDocOutline from './VPContentDocOutline.vue' import VPDocOutline from './VPDocOutline.vue'
import VPContentDocFooter from './VPContentDocFooter.vue' import VPDocFooter from './VPDocFooter.vue'
const { page } = useData() const { page } = useData()
@ -12,13 +12,13 @@ const pageName = computed(() => {
</script> </script>
<template> <template>
<div class="VPContentDoc has-aside"> <div class="VPDoc has-aside">
<div class="container"> <div class="container">
<div class="aside"> <div class="aside">
<div class="aside-container"> <div class="aside-container">
<div class="aside-curtain" /> <div class="aside-curtain" />
<div class="aside-content"> <div class="aside-content">
<VPContentDocOutline v-if="page.headers" /> <VPDocOutline v-if="page.headers" />
</div> </div>
</div> </div>
</div> </div>
@ -28,49 +28,49 @@ const pageName = computed(() => {
<Content class="vp-doc" :class="pageName" /> <Content class="vp-doc" :class="pageName" />
</main> </main>
<VPContentDocFooter /> <VPDocFooter />
</div> </div>
</div> </div>
</div> </div>
</template> </template>
<style scoped> <style scoped>
.VPContentDoc { .VPDoc {
padding: 32px 24px 96px; padding: 32px 24px 96px;
} }
@media (min-width: 768px) { @media (min-width: 768px) {
.VPContentDoc { .VPDoc {
padding: 48px 32px 128px; padding: 48px 32px 128px;
} }
} }
@media (min-width: 960px) { @media (min-width: 960px) {
.VPContentDoc { .VPDoc {
padding: 32px 64px 96px; padding: 32px 64px 96px;
} }
} }
@media (min-width: 1280px) { @media (min-width: 1280px) {
.VPContentDoc { .VPDoc {
padding: 32px 0 128px 64px; padding: 32px 0 128px 64px;
} }
.VPContentDoc:not(.has-sidebar.has-aside) { .VPDoc:not(.has-sidebar.has-aside) {
padding-left: calc((100vw - 688px) / 2); padding-left: calc((100vw - 688px) / 2);
} }
.VPContentDoc.has-aside:not(.has-sidebar) { .VPDoc.has-aside:not(.has-sidebar) {
padding-left: calc((100vw - 688px - 320px) / 2); padding-left: calc((100vw - 688px - 320px) / 2);
} }
.VPContentDoc:not(.has-aside) .content { .VPDoc:not(.has-aside) .content {
min-width: 688px; min-width: 688px;
} }
} }
@media (min-width: 1440px) { @media (min-width: 1440px) {
.VPContentDoc { .VPDoc {
padding: 32px 0 128px 96px; padding: 32px 0 128px 96px;
} }
} }

@ -13,7 +13,7 @@ const control = usePrevNext()
</script> </script>
<template> <template>
<footer v-if="control.prev || control.next" class="VPContentDocFooter"> <footer v-if="control.prev || control.next" class="VPDocFooter">
<div v-if="theme.editLink && frontmatter.editLink !== false" class="edit-link"> <div v-if="theme.editLink && frontmatter.editLink !== false" class="edit-link">
<VPLink class="edit-link-button" :href="editLink.url" :no-icon="true"> <VPLink class="edit-link-button" :href="editLink.url" :no-icon="true">
<VPIconEdit class="edit-link-icon" /> <VPIconEdit class="edit-link-icon" />
@ -39,7 +39,7 @@ const control = usePrevNext()
</template> </template>
<style scoped> <style scoped>
.VPContentDocFooter { .VPDocFooter {
margin-top: 64px; margin-top: 64px;
} }
@ -94,6 +94,7 @@ const control = usePrevNext()
} }
.pager.has-prev { .pager.has-prev {
padding-top: 0;
padding-left: 16px; padding-left: 16px;
} }
} }

@ -22,7 +22,7 @@ function handleClick({ target: el }: Event) {
</script> </script>
<template> <template>
<div class="VPContentDocOutline" ref="container"> <div class="VPDocOutline" ref="container">
<div class="outline-marker" ref="marker" /> <div class="outline-marker" ref="marker" />
<div class="outline-title">On this page</div> <div class="outline-title">On this page</div>
@ -54,7 +54,7 @@ function handleClick({ target: el }: Event) {
</template> </template>
<style scoped> <style scoped>
.VPContentDocOutline { .VPDocOutline {
position: relative; position: relative;
border-left: 1px solid var(--vp-c-divider-light); border-left: 1px solid var(--vp-c-divider-light);
padding-left: 16px; padding-left: 16px;

@ -0,0 +1,9 @@
<script setup lang="ts">
import VPHomeHero from './VPHomeHero.vue'
</script>
<template>
<div class="VPHome">
<VPHomeHero />
</div>
</template>

@ -0,0 +1,106 @@
<script setup lang="ts">
import { computed } from 'vue'
import { useData } from 'vitepress'
const { frontmatter: fm } = useData()
</script>
<template>
<div v-if="fm.hero" class="VPHomeHero">
<div class="container">
<p v-if="fm.hero.name" class="name">{{ fm.hero.name }}</p>
<h1 v-if="fm.hero.text" class="text">{{ fm.hero.text }}</h1>
<p v-if="fm.hero.tagline" class="tagline">{{ fm.hero.tagline }}</p>
</div>
</div>
</template>
<style scoped>
.VPHomeHero {
margin-top: calc(var(--vp-nav-height) * -1);
padding: calc(var(--vp-nav-height) + 48px) 24px 56px;
}
@media (min-width: 640px) {
.VPHomeHero {
padding: calc(var(--vp-nav-height) + 80px) 48px 96px;
}
}
@media (min-width: 960px) {
.VPHomeHero {
padding: calc(var(--vp-nav-height) + 80px) 32px 96px;
}
}
.container {
margin: 0 auto;
max-width: 960px;
}
.name,
.text {
line-height: 44px;
font-size: 32px;
font-weight: 700;
}
.name {
padding-bottom: 4px;
color: var(--vp-c-brand);
}
@media (min-width: 640px) {
.name,
.text {
max-width: 920px;
line-height: 56px;
font-size: 48px;
font-weight: 600;
}
.name {
padding-bottom: 6px;
}
}
@media (min-width: 960px) {
.name,
.text {
max-width: 920px;
line-height: 72px;
font-size: 64px;
font-weight: 600;
}
.name {
padding-bottom: 6px;
}
}
.tagline {
padding-top: 16px;
line-height: 32px;
font-size: 18px;
font-weight: 500;
color: var(--vp-c-text-2);
}
@media (min-width: 640px) {
.tagline {
padding-top: 24px;
max-width: 540px;
line-height: 32px;
font-size: 20px;
}
}
@media (min-width: 960px) {
.tagline {
padding-top: 32px;
max-width: 720px;
line-height: 40px;
font-size: 24px;
}
}
</style>

@ -1,39 +1,52 @@
<script setup lang="ts"> <script setup lang="ts">
import { useData } from 'vitepress' import { useData } from 'vitepress'
import { useSidebar } from '../composables/sidebar'
const { site, theme } = useData() const { site, theme } = useData()
const { hasSidebar } = useSidebar()
</script> </script>
<template> <template>
<a class="VPNavBarTitle" href="/"> <div class="VPNavBarTitle" :class="{ 'has-sidebar': hasSidebar }">
<img v-if="theme.logo" class="logo" :src="theme.logo" :alt="site.title"> <a class="title" href="/">
{{ site.title }} <img v-if="theme.logo" class="logo" :src="theme.logo" :alt="site.title">
</a> {{ site.title }}
</a>
</div>
</template> </template>
<style scoped> <style scoped>
.VPNavBarTitle { .VPNavBarTitle {
flex-shrink: 0;
}
@media (min-width: 960px) {
.VPNavBarTitle.has-sidebar {
margin-right: 32px;
width: calc(var(--vp-sidebar-width) - 64px);
border-bottom: 1px solid var(--vp-c-divider-light);
background-color: var(--vp-c-bg-sidebar);
}
}
.title {
display: flex; display: flex;
align-items: center; align-items: center;
height: var(--vp-nav-height-mobile); width: 100%;
height: var(--vp-nav-height);
font-size: 16px; font-size: 16px;
font-weight: 600; font-weight: 600;
color: var(--vp-c-text-1); color: var(--vp-c-text-1);
transition: background-color 0.5s, opacity 0.25s; transition: opacity 0.25s;
} }
.VPNavBarTitle:hover { .title:hover {
opacity: 0.6; opacity: 0.6;
} }
@media (min-width: 960px) { @media (min-width: 960px) {
.VPNavBarTitle { .title {
flex-shrink: 0; flex-shrink: 0;
margin-right: 32px;
border-bottom: 1px solid var(--vp-c-divider-light);
width: calc(var(--vp-sidebar-width) - 64px);
height: var(--vp-nav-height-desktop);
background-color: var(--vp-c-bg-sidebar);
} }
} }

@ -0,0 +1,5 @@
<template>
<div class="VPPage">
<Content />
</div>
</template>

@ -39,6 +39,9 @@ h4,
h5, h5,
h6 { h6 {
margin: 0; margin: 0;
line-height: 24px;
font-size: 16px;
font-weight: 400;
} }
p { p {

@ -185,10 +185,17 @@
* -------------------------------------------------------------------------- */ * -------------------------------------------------------------------------- */
:root { :root {
--vp-nav-height: var(--vp-nav-height-mobile);
--vp-nav-height-mobile: 56px; --vp-nav-height-mobile: 56px;
--vp-nav-height-desktop: 72px; --vp-nav-height-desktop: 72px;
} }
@media (min-width: 960px) {
:root {
--vp-nav-height: var(--vp-nav-height-desktop);
}
}
/** /**
* Component: Sidebar * Component: Sidebar
* -------------------------------------------------------------------------- */ * -------------------------------------------------------------------------- */
@ -212,3 +219,15 @@
.dark { .dark {
--vp-code-block-bg: var(--vp-c-bg-sidebar); --vp-code-block-bg: var(--vp-c-bg-sidebar);
} }
/**
* Component: Home
* -------------------------------------------------------------------------- */
:root {
}
.dark {
}

@ -123,6 +123,10 @@
list-style: disc; list-style: disc;
} }
.vp-doc li + li {
margin-top: 8px;
}
/** /**
* Table * Table
* -------------------------------------------------------------------------- */ * -------------------------------------------------------------------------- */
@ -331,6 +335,8 @@
.vp-doc [class~='language-ts']:before { content: 'ts'; } .vp-doc [class~='language-ts']:before { content: 'ts'; }
.vp-doc [class~='language-tsx']:before { content: 'tsx'; } .vp-doc [class~='language-tsx']:before { content: 'tsx'; }
.vp-doc [class~='language-json']:before { content: 'json'; } .vp-doc [class~='language-json']:before { content: 'json'; }
.vp-doc [class~='language-yaml']:before { content: 'yaml'; }
.vp-doc [class~='language-yml']:before { content: 'yaml'; }
.vp-doc [class~='language-sh']:before { content: 'sh'; } .vp-doc [class~='language-sh']:before { content: 'sh'; }
.vp-doc [class~='language-bash']:before { content: 'sh'; } .vp-doc [class~='language-bash']:before { content: 'sh'; }

Loading…
Cancel
Save