fix: make home page look better (#154)

pull/164/head
Kia King Ishii 5 years ago committed by GitHub
parent 77bb75f7ee
commit a084cd3f78
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -66,6 +66,10 @@ function getConfigSidebar() {
{ text: 'Basics', link: '/config/basics' },
{ text: 'Carbon Ads', link: '/config/carbon-ads' }
]
},
{
text: 'Theme Config',
children: [{ text: 'Homepage', link: '/config/homepage' }]
}
]
}

@ -0,0 +1,23 @@
# Theme Config: Homepage
VitePress provides a homepage layout. To use it, specify `home: true` plus some other metadata in your root `index.md`'s [YAML frontmatter](../guide/frontmatter). This is an example of how it works:
```yaml
---
home: true
heroImage: /logo.png
heroAlt: Logo image
heroText: Hero Title
tagline: Hero subtitle
actionText: Get Started
actionLink: /guide/
features:
- title: Simplicity First
details: Minimal setup with markdown-centered project structure helps you focus on writing.
- title: Vue-Powered
details: Enjoy the dev experience of Vue + webpack, use Vue components in markdown, and develop custom themes with Vue.
- title: Performant
details: VitePress generates pre-rendered static HTML for each page, and runs as an SPA once a page is loaded.
footer: MIT Licensed | Copyright © 2019-present Evan You
---
```

@ -0,0 +1,8 @@
import { Ref, computed } from 'vue'
import { usePageData } from './pageData'
export type FrontmatterRef = Ref<Record<string, any>>
export function useFrontmatter() {
return computed(() => usePageData().value.frontmatter)
}

@ -14,6 +14,7 @@ export { useRouter, useRoute, Router, Route } from './router'
export { useSiteData } from './composables/siteData'
export { useSiteDataByRoute } from './composables/siteDataByRoute'
export { usePageData } from './composables/pageData'
export { useFrontmatter } from './composables/frontmatter'
// components
export { Content } from './components/Content'

@ -18,7 +18,7 @@
</SideBar>
<!-- TODO: make this button accessible -->
<div class="sidebar-mask" @click="toggleSidebar(false)" />
<main class="home" aria-labelledby="main-title" v-if="enableHome">
<main class="main-home" aria-labelledby="main-title" v-if="enableHome">
<Home>
<template #hero>
<slot name="home-hero" />

@ -1,184 +1,14 @@
<template>
<header class="hero">
<img
v-if="data.heroImage"
:src="$withBase(heroImageSrc)"
:alt="data.heroAlt || 'hero'"
/>
<h1 v-if="data.heroText !== null" id="main-title">
{{ data.heroText || siteTitle || 'Hello' }}
</h1>
<p v-if="data.tagline !== null" class="description">
{{ data.tagline || siteDescription || 'Welcome to your VitePress site' }}
</p>
<p v-if="data.actionText && data.actionLink" class="action">
<a class="action-link" :href="actionLink.link">
{{ actionLink.text }}
</a>
</p>
<slot name="hero" />
</header>
<div v-if="data.features && data.features.length" class="features">
<div v-for="(feature, index) in data.features" :key="index" class="feature">
<h2>{{ feature.title }}</h2>
<p>{{ feature.details }}</p>
</div>
<slot name="features" />
</div>
<div v-if="data.footer" class="footer">
{{ data.footer }}
<slot name="footer" />
</div>
<HomeHero />
<slot name="hero" />
<HomeFeatures />
<slot name="features" />
<HomeFooter />
<slot name="footer" />
</template>
<script setup lang="ts">
import { computed } from 'vue'
import { useRoute, useSiteData } from 'vitepress'
const route = useRoute()
const siteData = useSiteData()
const data = computed(() => route.data.frontmatter)
const actionLink = computed(() => ({
link: data.value.actionLink,
text: data.value.actionText
}))
const heroImageSrc = computed(() => data.value.heroImage)
const siteTitle = computed(() => siteData.value.title)
const siteDescription = computed(() => siteData.value.description)
import HomeHero from './HomeHero.vue'
import HomeFeatures from './HomeFeatures.vue'
import HomeFooter from './HomeFooter.vue'
</script>
<style scoped>
.hero {
text-align: center;
}
.hero img {
max-width: 100%;
max-height: 280px;
display: block;
margin: 3rem auto 1.5rem;
}
.hero h1 {
font-size: 3rem;
}
.hero h1,
.hero .description,
.hero .action {
margin: 1.8rem auto;
}
.hero .description {
max-width: 35rem;
font-size: 1.6rem;
line-height: 1.3;
/* TODO: calculating lighten 40% color with using style :vars from `--c-text` */
color: #6a8bad;
}
.action-link {
display: inline-block;
border-radius: 4px;
padding: 0 20px;
line-height: 48px;
font-size: 1rem;
font-weight: 500;
color: #ffffff;
background-color: var(--c-brand);
transition: background-color .1s ease;
}
.action-link:hover {
text-decoration: none;
background-color: var(--c-brand-light);
}
@media (min-width: 420px) {
.action-link {
padding: 0 24px;
line-height: 56px;
font-size: 1.2rem;
font-weight: 500;
}
}
.features {
border-top: 1px solid var(--c-divider);
padding: 1.2rem 0;
margin-top: 2.5rem;
display: flex;
flex-wrap: wrap;
align-items: flex-start;
align-content: stretch;
justify-content: space-between;
}
.feature {
flex-grow: 1;
flex-basis: 30%;
max-width: 30%;
}
.feature h2 {
font-size: 1.4rem;
font-weight: 500;
border-bottom: none;
padding-bottom: 0;
/* TODO: calculating lighten 10% color with using style :vars from `--c-text` */
color: #3a5169;
}
.feature p {
/* TODO: calculating lighten 25% color with using style :vars from `--c-text` */
color: #4e6e8e;
}
.footer {
padding: 2.5rem;
border-top: 1px solid var(--c-divider);
text-align: center;
/* TODO: calculating lighten 25% color with using style :vars from `--c-text` */
color: #4e6e8e;
}
@media screen and (max-width: 719px) {
.features {
flex-direction: column;
}
.feature {
max-width: 100%;
padding: 0 2.5rem;
}
}
@media screen and (max-width: 429px) {
.hero img {
max-height: 210px;
margin: 2rem auto 1.2rem;
}
.hero h1 {
font-size: 2rem;
}
.hero h1,
.hero .description {
margin: 1.2rem auto;
}
.hero .description {
font-size: 1.2rem;
}
.feature h2 {
font-size: 1.25rem;
}
}
</style>

@ -0,0 +1,129 @@
<template>
<div v-if="hasFeatures" class="home-features">
<div class="wrapper">
<div class="container">
<div class="features">
<section v-for="(feature, index) in features" :key="index" class="feature">
<h2 class="title" v-if="feature.title">{{ feature.title }}</h2>
<p class="details" v-if="feature.details">{{ feature.details }}</p>
</section>
</div>
</div>
</div>
</div>
</template>
<script setup lang="ts">
import { computed } from 'vue'
import { useSiteDataByRoute, useFrontmatter } from 'vitepress'
ref: data = useFrontmatter()
ref: hasFeatures = computed(() => data.features && data.features.length > 0)
ref: features = computed(() => data.features ? data.features : [])
</script>
<style scoped>
.home-features {
margin: 0 auto;
padding: 2.5rem 0 2.75rem;
max-width: 960px;
}
.home-hero + .home-features {
padding-top: 0;
}
@media (min-width: 420px) {
.home-features {
padding: 3.25rem 0 3.5rem;
}
.home-hero + .home-features {
padding-top: 0;
}
}
@media (min-width: 720px) {
.home-features {
padding-right: 1.5rem;
padding-left: 1.5rem;
}
}
.wrapper {
padding: 0 1.5rem;
}
.home-hero + .home-features .wrapper {
border-top: 1px solid var(--c-divider);
padding-top: 2.5rem;
}
@media (min-width: 420px) {
.home-hero + .home-features .wrapper {
padding-top: 3.25rem;
}
}
@media (min-width: 720px) {
.wrapper {
padding-right: 0;
padding-left: 0;
}
}
.container {
margin: 0 auto;
max-width: 392px;
}
@media (min-width: 720px) {
.container {
max-width: 960px;
}
}
.features {
display: flex;
flex-wrap: wrap;
margin: -20px -24px;
}
.feature {
flex-shrink: 0;
padding: 20px 24px;
width: 100%;
}
@media (min-width: 720px) {
.feature {
width: calc(100% / 3);
}
}
.title {
margin: 0;
border-bottom: 0;
line-height: 1.4;
font-size: 1.25rem;
font-weight: 500;
}
@media (min-width: 420px) {
.title {
font-size: 1.4rem;
}
}
.details {
margin: 0;
line-height: 1.6;
font-size: 1rem;
color: var(--c-text-light);
}
.title + .details {
padding-top: 0.25rem;
}
</style>

@ -0,0 +1,44 @@
<template>
<footer v-if="$frontmatter.footer" class="footer">
<div class="container">
<p class="text">{{ $frontmatter.footer }}</p>
</div>
</footer>
</template>
<style scoped>
.footer {
margin: 0 auto;
max-width: 960px;
}
@media (min-width: 720px) {
.footer {
padding: 0 1.5rem;
}
}
.container {
padding: 2rem 1.5rem 2.25rem;
}
.home-hero + .footer .container,
.home-features + .footer .container,
.home-content + .footer .container {
border-top: 1px solid var(--c-divider);
}
@media (min-width: 420px) {
.container {
padding: 3rem 1.5rem 3.25rem;
}
}
.text {
margin: 0;
text-align: center;
line-height: 1.4;
font-size: .9rem;
color: var(--c-text-light);
}
</style>

@ -0,0 +1,140 @@
<template>
<header v-if="showHero" class="home-hero">
<figure v-if="$frontmatter.heroImage" class="figure">
<img
class="image"
:src="$withBase($frontmatter.heroImage)"
:alt="$frontmatter.heroAlt"
>
</figure>
<h1 v-if="hasHeroText" class="title">{{ heroText }}</h1>
<p v-if="hasTagline" class="description">{{ tagline }}</p>
<div v-if="hasAction" class="action">
<a class="action-link" :href="$frontmatter.actionLink">
{{ $frontmatter.actionText }}
</a>
</div>
</header>
</template>
<script setup lang="ts">
import { computed } from 'vue'
import { useSiteDataByRoute, useFrontmatter } from 'vitepress'
ref: site = useSiteDataByRoute()
ref: data = useFrontmatter()
ref: showHero = computed(() => {
return data.heroImage || hasHeroText || hasTagline || hasAction
})
ref: hasHeroText = computed(() => data.heroText !== null)
ref: heroText = computed(() => data.heroText || site.title)
ref: hasTagline = computed(() => data.tagline !== null)
ref: tagline = computed(() => data.tagline || site.description)
ref: hasAction = computed(() => data.actionLink && data.actionText)
</script>
<style scoped>
.home-hero {
margin: 2.5rem 0 2.75rem;
padding: 0 1.5rem;
text-align: center;
}
@media (min-width: 420px) {
.home-hero {
margin: 3.5rem 0;
}
}
@media (min-width: 720px) {
.home-hero {
margin: 4rem 0 4.25rem;
}
}
.figure {
padding: 0 1.5rem;
}
.image {
display: block;
margin: 0 auto;
width: auto;
max-width: 100%;
max-height: 280px;
}
.title {
margin-top: 1.5rem;
font-size: 2rem;
}
@media (min-width: 420px) {
.title {
font-size: 3rem;
}
}
@media (min-width: 720px) {
.title {
margin-top: 2rem;
}
}
.description {
margin: 0;
margin-top: .25rem;
line-height: 1.3;
font-size: 1.2rem;
color: var(--c-text-light);
}
@media (min-width: 420px) {
.description {
line-height: 1.2;
font-size: 1.6rem;
}
}
.action {
margin-top: 1.5rem;
}
@media (min-width: 420px) {
.action {
margin-top: 2rem;
}
}
.action-link {
display: inline-block;
border-radius: 4px;
padding: 0 20px;
line-height: 48px;
font-size: 1rem;
font-weight: 500;
color: #ffffff;
background-color: var(--c-brand);
transition: background-color .1s ease;
}
.action-link:hover {
text-decoration: none;
background-color: var(--c-brand-light);
}
@media (min-width: 420px) {
.action-link {
padding: 0 24px;
line-height: 56px;
font-size: 1.2rem;
font-weight: 500;
}
}
</style>

@ -144,6 +144,10 @@ a.header-anchor:focus {
text-decoration: none;
}
figure {
margin: 0;
}
img {
max-width: 100%;
}
@ -257,6 +261,10 @@ blockquote > p {
.theme main {
padding-left: 20rem;
}
.theme main.main-home {
padding-left: 0;
}
}
@media screen and (min-width: 720px) {
@ -279,17 +287,3 @@ blockquote > p {
margin-left: 0;
}
}
.theme main.home {
padding: var(--header-height) 2rem 0;
max-width: 960px;
margin: 0px auto;
display: block;
}
@media screen and (max-width: 429px) {
.theme main.home {
padding-left: 1.5rem;
padding-right: 1.5rem;
}
}

Loading…
Cancel
Save