Re-creation of marp.app (#36)

* Re-creation of marp.app

* Set correct host in Netlify's deploy preview

* Prevent purging Tailwind classes used in utils

* Update glob path for purging CSS

* Don't build a docs page working in progress
This commit is contained in:
Yuki Hattori 2020-08-22 23:01:40 +09:00 committed by GitHub
parent 1f8de9b97a
commit 469b666f91
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
91 changed files with 6891 additions and 4732 deletions

View File

@ -1,82 +0,0 @@
version: 2.1
executors:
node:
parameters:
version:
type: string
default: lts
docker:
- image: circleci/node:<< parameters.version >>
working_directory: ~/marp
commands:
install:
parameters:
postinstall:
type: steps
default: []
yarn:
type: string
default: 1.22.4
steps:
- run:
name: Upgrade yarn for current user
command: cd ~ && yarn policies set-version << parameters.yarn >>
- restore_cache:
keys:
- v2.2-dependencies-{{ .Environment.CIRCLE_JOB }}-{{ checksum "yarn.lock" }}-{{ .Branch }}
- v2.2-dependencies-{{ .Environment.CIRCLE_JOB }}-{{ checksum "yarn.lock" }}-
- v2.2-dependencies-{{ .Environment.CIRCLE_JOB }}-
- run: yarn install
- steps: << parameters.postinstall >>
- save_cache:
key: v2.2-dependencies-{{ .Environment.CIRCLE_JOB }}-{{ checksum "yarn.lock" }}-{{ .Branch }}
paths:
- ~/.cache/yarn
audit:
steps:
- checkout
- install:
postinstall:
- run: yarn audit
test:
steps:
- run: node --version
- checkout
- install
- run:
name: Prettier formatting
command: yarn check:format
- run:
name: ESLint
command: yarn lint
jobs:
audit:
executor: node
steps:
- audit
test-node:
executor:
name: node
version: '12.16.3' # Specify LTS version for development
steps:
- test
workflows:
test:
jobs:
- audit
- test-node:
requires:
- audit

View File

@ -1,4 +1,5 @@
.cache/
dist
.next
coverage
lib
node_modules
tmp
out

40
.eslintrc.js Normal file
View File

@ -0,0 +1,40 @@
const path = require('path')
const { workspaces } = require('./package.json')
module.exports = {
root: true,
env: {
browser: true,
node: true,
},
extends: ['eslint:recommended', 'plugin:import/recommended', 'prettier'],
rules: {
'import/order': ['error', { alphabetize: { order: 'asc' } }],
},
overrides: [
{
files: ['**/*.ts', '**/*.tsx'],
parser: '@typescript-eslint/parser',
plugins: ['@typescript-eslint'],
extends: [
'plugin:@typescript-eslint/recommended',
'plugin:import/typescript',
'prettier/@typescript-eslint',
],
rules: {
'@typescript-eslint/no-explicit-any': 'off',
'@typescript-eslint/explicit-function-return-type': 'off',
'@typescript-eslint/explicit-module-boundary-types': 'off',
},
settings: {
'import/resolver': {
typescript: {
project: ['', ...workspaces].map((dir) =>
path.join(dir, 'tsconfig.json')
),
},
},
},
},
],
}

View File

@ -1,29 +0,0 @@
env:
browser: true
es6: true
node: true
extends:
- eslint:recommended
- plugin:import/recommended
- plugin:import/react
- plugin:react/recommended
- prettier
- prettier/react
root: true
settings:
import/resolver:
node:
extensions:
- .js
- .jsx
react:
version: detect
rules:
import/order:
- error
- alphabetize:
order: asc

21
.github/dependabot.yml vendored Normal file
View File

@ -0,0 +1,21 @@
version: 2
updates:
- package-ecosystem: npm
directory: '/'
reviewers:
- 'marp-team/maintainers'
schedule:
interval: daily
allow:
- dependency-name: '@marp-team/*'
versioning-strategy: increase
- package-ecosystem: github-actions
directory: '/'
reviewers:
- 'marp-team/maintainers'
schedule:
interval: weekly
# versioning-strategy: increase-if-necessary
open-pull-requests-limit: 0 # Dependabot does not allow relaxed versioning :(

54
.github/workflows/test.yml vendored Normal file
View File

@ -0,0 +1,54 @@
name: Test
on:
- pull_request
- push
env:
CACHE_PREFIX: v1
YARN_VERSION: '^1.22.4'
jobs:
validate:
runs-on: ubuntu-latest
if: ${{ github.ref == 'refs/heads/master' || !startsWith(github.event.head_commit.message, '[ci skip]') }}
steps:
- run: echo "${{ github.event.head_commit.message }}"
test:
runs-on: ubuntu-latest
needs: validate
steps:
- uses: actions/checkout@v2
- name: Detect Node version from .nvmrc
id: node_version
run: echo "::set-output name=nvmrc::$(cat .nvmrc)"
- uses: actions/setup-node@v2-beta
with:
node-version: ${{ steps.node_version.outputs.nvmrc }}
- name: Install yarn
id: yarn
run: |
cd $HOME && yarn policies set-version $YARN_VERSION
echo "::set-output name=cache_dir::$(yarn cache dir)"
- uses: actions/cache@v2
with:
path: ${{ steps.yarn.outputs.cache_dir }}
key: yarn_cache-${{ env.CACHE_PREFIX }}-${{ hashFiles('**/yarn.lock') }}
restore-keys: yarn_cache-${{ env.CACHE_PREFIX }}-
- run: yarn install
- run: yarn audit
- name: Prettier formatting
run: yarn check:format
- name: ESLint
run: yarn lint:js
- name: TypeScript type checking
run: yarn check:ts

66
.gitignore vendored
View File

@ -1,6 +1,7 @@
tmp
out
# Created by https://www.gitignore.io/api/node,windows,macos,linux,sublimetext,emacs,vim,visualstudiocode
# Created by https://www.toptal.com/developers/gitignore/api/node,windows,macos,linux,sublimetext,emacs,vim,visualstudiocode
# Edit at https://www.toptal.com/developers/gitignore?templates=node,windows,macos,linux,sublimetext,emacs,vim,visualstudiocode
### Emacs ###
# -*- mode: gitignore; -*-
@ -49,6 +50,10 @@ flycheck_*.el
# directory configuration
.dir-locals.el
# network security
/network-security.data
### Linux ###
# temporary files which can be created if a process still has a handle open of a deleted file
@ -98,6 +103,10 @@ logs
npm-debug.log*
yarn-debug.log*
yarn-error.log*
lerna-debug.log*
# Diagnostic reports (https://nodejs.org/api/report.html)
report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json
# Runtime data
pids
@ -110,11 +119,12 @@ lib-cov
# Coverage directory used by tools like istanbul
coverage
*.lcov
# nyc test coverage
.nyc_output
# Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files)
# Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files)
.grunt
# Bower dependency directory (https://bower.io/)
@ -133,12 +143,21 @@ jspm_packages/
# TypeScript v1 declaration files
typings/
# TypeScript cache
*.tsbuildinfo
# Optional npm cache directory
.npm
# Optional eslint cache
.eslintcache
# Microbundle cache
.rpt2_cache/
.rts2_cache_cjs/
.rts2_cache_es/
.rts2_cache_umd/
# Optional REPL history
.node_repl_history
@ -150,21 +169,41 @@ typings/
# dotenv environment variables file
.env
.env.test
# parcel-bundler cache (https://parceljs.org/)
.cache
# next.js build output
# Next.js build output
.next
# nuxt.js build output
# Nuxt.js build / generate output
.nuxt
dist
# Gatsby files
.cache/
# Comment in the public line in if your project uses Gatsby and not Next.js
# https://nextjs.org/blog/next-9-1#public-directory-support
# public
# vuepress build output
.vuepress/dist
# Serverless directories
.serverless
.serverless/
# FuseBox cache
.fusebox/
# DynamoDB Local files
.dynamodb/
# TernJS port file
.tern-port
# Stores VSCode versions used for testing VSCode extensions
.vscode-test
### SublimeText ###
# Cache files for Sublime Text
@ -201,6 +240,7 @@ GitHub.sublime-settings
### Vim ###
# Swap
[._]*.s[a-v][a-z]
!*.svg # comment out if you don't need vector files
[._]*.sw[a-p]
[._]s[a-rt-v][a-z]
[._]ss[a-gi-z]
@ -208,6 +248,7 @@ GitHub.sublime-settings
# Session
Session.vim
Sessionx.vim
# Temporary
.netrwhist
@ -218,14 +259,16 @@ tags
### VisualStudioCode ###
.vscode/*
!.vscode/settings.json
!.vscode/tasks.json
!.vscode/launch.json
!.vscode/extensions.json
*.code-workspace
### VisualStudioCode Patch ###
# Ignore all local history of files
.history
### Windows ###
# Windows thumbnail cache files
Thumbs.db
Thumbs.db:encryptable
ehthumbs.db
ehthumbs_vista.db
@ -248,5 +291,4 @@ $RECYCLE.BIN/
# Windows shortcuts
*.lnk
# End of https://www.gitignore.io/api/node,windows,macos,linux,sublimetext,emacs,vim,visualstudiocode
# End of https://www.toptal.com/developers/gitignore/api/node,windows,macos,linux,sublimetext,emacs,vim,visualstudiocode

2
.nvmrc
View File

@ -1 +1 @@
12.16.3
12.18.3

View File

@ -1,6 +1,3 @@
.git/
.cache/
.vscode/
dist
.next
node_modules
tmp
out

View File

@ -1,6 +1,6 @@
MIT License
Copyright (c) 2018- Marp team (marp-team@marp.app)
Copyright (c) 2018-2020 Marp team (marp-team@marp.app)
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal

View File

@ -1,6 +1,6 @@
<div align="center">
<p>
<img src="marp.png" alt="Marp" width="500" />
<a href="https://marp.app/"><img src="marp.png" alt="Marp" width="500" /></a>
</p>
<p>
<strong>Marp</strong>: Markdown Presentation Ecosystem
@ -9,34 +9,63 @@
**Marp** is the ecosystem to write your presentation with plain Markdown.
<div align="center">
### [Go to the official website ▶︎](https://marp.app)
</div>
## Marp family
Our project is spread over many repos in order to focus on a limited scope per repository. Active projects are shown as emphasized in following list.
Our project is spread over many repos in order to focus on a limited scope per repository.
This repo (**[@marp-team/marp][marp]**) is an entrance to the Marp family, and places [our website](https://marp.app/).
This repo (**[marp-team/marp][marp]**) is an entrance to the Marp family, and places [our website](https://marp.app/) in `/website`.
### Framework / Core
| Name | Description | Release |
| -------------------------: | :--------------------------------------------------------------------------------- | :-------------------------------------------------------- |
| **[Marpit]** | The skinny framework for creating slide deck from Markdown. ([marpit.marp.app]) | [![@marp-team/marpit][badge-marpit]][marpit-npm] |
| **[Marp Core][marp-core]** | The core of Marp converter with practical features and [themes][marp-core-themes]. | [![@marp-team/marp-core][badge-marp-core]][marp-core-npm] |
| Name | Description | Release |
| -------------------------: | :------------------------------------------------------------------------------------------ | :-------------------------------------------------------- |
| **[Marpit]** | The skinny framework for creating slide deck from Markdown. ([marpit.marp.app]) | [![@marp-team/marpit][badge-marpit]][marpit-npm] |
| **[Marp Core][marp-core]** | The core of Marp converter with practical features and [built-in themes][marp-core-themes]. | [![@marp-team/marp-core][badge-marp-core]][marp-core-npm] |
### Apps
| Name | Description | Release |
| -----------------------: | :----------------------------------------------------------------------------------------------- | :----------------------------------------------------- |
| **[Marp CLI][marp-cli]** | [Marp Core][marp-core] / [Marpit]'s CLI interface to convert into HTML, PDF, PPTX, and image(s). | [![@marp-team/marp-cli][badge-marp-cli]][marp-cli-npm] |
| [Marp Web][marp-web] | The main interface of Marp based on [PWA] and [Preact] framework. | [![tech demo][badge-marp-web]][marp-web-site] |
| Marp Desktop | The desktop client for [Marp Web][marp-web-site] for replacing [yhatt/marp]. | ![PLANNED][badge-planned] |
### Integrations
| Name | Description | Release |
| -----------------------------: | :-------------------------------------------------------------------------------- | :----------------------------------------------------------- |
| **[Marp VSCode][marp-vscode]** | A [VS Code][vscode] extension to preview the slide deck written in Marp Markdown. | [![VS Marketplace][badge-marp-vscode]][marp-vscode-release] |
| [Marp React][marp-react] | Marp renderer component for [React]. | [![@marp-team/marp-react][badge-marp-react]][marp-react-npm] |
| [Marp Vue][marp-vue] | Marp renderer component for [Vue]. | [![@marp-team/marp-vue][badge-marp-vue]][marp-vue-npm] |
| Name | Description | Release |
| ----------------------------------: | :-------------------------------------------------------------------------------- | :---------------------------------------------------------- |
| **[Marp for VS Code][marp-vscode]** | A [VS Code][vscode] extension to preview the slide deck written in Marp Markdown. | [![VS Marketplace][badge-marp-vscode]][marp-vscode-release] |
<details>
<summary><b>See outdated/inactive projects...</b></summary><br />
| Name | Description | Release |
| -----------------------: | :--------------------------------------------------------------- | :----------------------------------------------------------- |
| [Marp Web][marp-web] | The Web interface of Marp based on [PWA] and [Preact] framework. | [![tech demo][badge-marp-web]][marp-web-site] |
| [Marp React][marp-react] | Marp renderer component for [React]. | [![@marp-team/marp-react][badge-marp-react]][marp-react-npm] |
| [Marp Vue][marp-vue] | Marp renderer component for [Vue]. | [![@marp-team/marp-vue][badge-marp-vue]][marp-vue-npm] |
And there is a gravesite of classic Marp app in https://github.com/yhatt/marp. :ghost:
[marp-web]: https://github.com/marp-team/marp-web
[marp-react]: https://github.com/marp-team/marp-react
[marp-vue]: https://github.com/marp-team/marp-vue
[pwa]: https://en.wikipedia.org/wiki/Progressive_Web_Apps
[preact]: https://preactjs.com/
[react]: https://reactjs.org/
[vue]: https://vuejs.org/
[marp-web-site]: https://web.marp.app/
[marp-react-npm]: https://www.npmjs.com/package/@marp-team/marp-react
[marp-vue-npm]: https://www.npmjs.com/package/@marp-team/marp-vue
[badge-marp-web]: https://img.shields.io/badge/%E2%80%8B-tech%20demo-%230288d1.svg?style=flat-square&logo=data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAA4AAAAOCAYAAAAfSC3RAAAAUUlEQVQokWNgGD6AqePif3Sx9B2PMcQwNKFrTN/x+D9ejTBNyBphmnBqRNYE04isCatGdE1MHRf/o2vC0IhNE1PaXPwacWnCqxGfJoI2Dn4AAN0ZrMM1VUFvAAAAAElFTkSuQmCC
[badge-marp-react]: https://img.shields.io/npm/v/@marp-team/marp-react.svg?style=flat-square&logo=npm
[badge-marp-vue]: https://img.shields.io/npm/v/@marp-team/marp-vue.svg?style=flat-square&logo=npm
</details>
[yhatt/marp]: https://github.com/yhatt/marp
[marp]: https://github.com/marp-team/marp
@ -44,37 +73,21 @@ This repo (**[@marp-team/marp][marp]**) is an entrance to the Marp family, and p
[marp-core]: https://github.com/marp-team/marp-core
[marp-core-themes]: https://github.com/marp-team/marp-core/tree/master/themes
[marp-cli]: https://github.com/marp-team/marp-cli
[marp-web]: https://github.com/marp-team/marp-web
[marp-vscode]: https://github.com/marp-team/marp-vscode
[marp-react]: https://github.com/marp-team/marp-react
[marp-vue]: https://github.com/marp-team/marp-vue
[pwa]: https://en.wikipedia.org/wiki/Progressive_Web_Apps
[preact]: https://preactjs.com/
[electron]: https://electronjs.org/
[vscode]: https://code.visualstudio.com/
[react]: https://reactjs.org/
[vue]: https://vuejs.org/
[marpit.marp.app]: https://marpit.marp.app/
[marpit-npm]: https://www.npmjs.com/package/@marp-team/marpit
[marp-core-npm]: https://www.npmjs.com/package/@marp-team/marp-core
[marp-cli-npm]: https://www.npmjs.com/package/@marp-team/marp-cli
[marp-web-site]: https://web.marp.app/
[marp-vscode-release]: https://marketplace.visualstudio.com/items?itemName=marp-team.marp-vscode
[marp-react-npm]: https://www.npmjs.com/package/@marp-team/marp-react
[marp-vue-npm]: https://www.npmjs.com/package/@marp-team/marp-vue
[badge-marpit]: https://img.shields.io/npm/v/@marp-team/marpit.svg?style=flat-square&logo=npm
[badge-marp-core]: https://img.shields.io/npm/v/@marp-team/marp-core.svg?style=flat-square&logo=npm
[badge-marp-cli]: https://img.shields.io/npm/v/@marp-team/marp-cli.svg?style=flat-square&logo=npm
[badge-marp-web]: https://img.shields.io/badge/%E2%80%8B-tech%20demo-%230288d1.svg?style=flat-square&logo=data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAA4AAAAOCAYAAAAfSC3RAAAAUUlEQVQokWNgGD6AqePif3Sx9B2PMcQwNKFrTN/x+D9ejTBNyBphmnBqRNYE04isCatGdE1MHRf/o2vC0IhNE1PaXPwacWnCqxGfJoI2Dn4AAN0ZrMM1VUFvAAAAAElFTkSuQmCC
[badge-marp-vscode]: https://img.shields.io/visual-studio-marketplace/v/marp-team.marp-vscode.svg?style=flat-square&logo=visual-studio-code&label=Marketplace
[badge-marp-react]: https://img.shields.io/npm/v/@marp-team/marp-react.svg?style=flat-square&logo=npm
[badge-marp-vue]: https://img.shields.io/npm/v/@marp-team/marp-vue.svg?style=flat-square&logo=npm
[badge-planned]: https://img.shields.io/badge/-PLANNED-lightgrey.svg?style=flat-square
[badge-wip]: https://img.shields.io/badge/-Work%20in%20progress-lightgrey.svg?style=flat-square
## Examples
### Starter
### Starter by Marp author
- **[Marp CLI example](https://yhatt-marp-cli-example.netlify.com/)** by [@yhatt](https://github.com/yhatt) - A good starter to write and host Marp slide with [GitPitch](https://gitpitch.com/) style powered by [Netlify](https://www.netlify.com/). (https://github.com/yhatt/marp-cli-example)
@ -88,8 +101,6 @@ This repo (**[@marp-team/marp][marp]**) is an entrance to the Marp family, and p
Let us know if you have created an awesome slide deck with Marp ecosystem! [Edit README.md and send pull request.](https://github.com/marp-team/marp/edit/master/README.md)
<!-- NOTE: The slide deck created by outdated yhatt/marp desktop app cannot add to examples. -->
## Contributing
Marp and sub-projects are following the [contributing guideline of marp-team][contributing]. Please read this before starting work in our projects.

View File

@ -1,6 +1,6 @@
{
"version": "0.0.0",
"packages": ["packages/*"],
"packages": ["packages/*", "website"],
"npmClient": "yarn",
"useWorkspaces": true
}

BIN
marp.png

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.7 KiB

After

Width:  |  Height:  |  Size: 3.4 KiB

View File

@ -1,3 +1,3 @@
[build]
publish = "packages/website/dist"
command = "yarn workspace @marp-team/marp-website build"
publish = "website/out"
command = "yarn workspace @marp-team/marp-website export"

View File

@ -1,6 +1,6 @@
{
"name": "@marp-team/marp",
"description": "The entrance repository of Marp family",
"description": "The entrance repository of Markdown presentation ecosystem",
"private": true,
"license": "MIT",
"author": {
@ -18,7 +18,8 @@
"url": "https://github.com/marp-team/marp"
},
"workspaces": [
"packages/*"
"packages/*",
"website"
],
"prettier": {
"semi": false,
@ -26,16 +27,23 @@
},
"scripts": {
"check:format": "yarn -s format -c",
"format": "prettier \"**/*.{css,html,js,json,jsx,md,scss,yaml,yml}\"",
"lint": "eslint \"**/*.{js,jsx}\"",
"website": "yarn -s workspace @marp-team/marp-website serve"
"check:ts": "lerna run --parallel check:ts",
"format:write": "yarn -s format --write",
"format": "prettier \"**/*.{css,js,jsx,json,md,mdx,scss,ts,tsx,yaml,yml}\"",
"lint:js": "eslint --ext .js,.jsx,.ts,.tsx --report-unused-disable-directives --cache .",
"website": "yarn workspace @marp-team/marp-website dev"
},
"devDependencies": {
"eslint": "^7.0.0",
"@tsconfig/recommended": "^1.0.1",
"@types/node": "^12.12.54",
"@typescript-eslint/eslint-plugin": "^3.9.1",
"@typescript-eslint/parser": "^3.9.1",
"eslint": "^7.7.0",
"eslint-config-prettier": "^6.11.0",
"eslint-plugin-import": "^2.20.2",
"eslint-plugin-react": "^7.20.0",
"lerna": "^3.21.0",
"prettier": "^2.0.5"
"eslint-import-resolver-typescript": "^2.2.1",
"eslint-plugin-import": "^2.22.0",
"lerna": "^3.22.1",
"prettier": "^2.0.5",
"typescript": "^4.0.2"
}
}

View File

@ -1,6 +0,0 @@
rules:
react/prop-types: 0
react/react-in-jsx-scope: 0 # Charge does not require to import React
import/extensions: # Charge cannot resolve dependency correctly if omitted extension
- error
- always

View File

@ -1,27 +0,0 @@
{
"name": "@marp-team/marp-website",
"version": "0.0.0",
"private": true,
"license": "MIT",
"author": {
"name": "Marp team",
"url": "https://github.com/marp-team"
},
"repository": {
"type": "git",
"url": "https://github.com/marp-team/marp"
},
"scripts": {
"build": "cross-env NODE_ENV=production charge build src dist",
"serve": "cross-env NODE_ENV=development charge serve src"
},
"devDependencies": {
"@emotion/core": "^10.0.28",
"@marp-team/marp-core": "^1.1.0",
"@static/charge": "^1.7.0",
"cross-env": "^7.0.2",
"github-slugger": "^1.3.0",
"highlight.js": "^10.0.3",
"react-innertext": "^1.1.5"
}
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 631 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 58 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 142 KiB

View File

@ -1,97 +0,0 @@
/** @jsx jsx */
import { jsx, css } from '@emotion/core'
import { Meta, blogStyle } from './blog.jsx'
import { Heading } from './components/heading.js.jsx'
import { Layout, contentStyle, generateTitle } from './layout.jsx'
export default function Blog({ environment, pages }) {
const articles = pages
.filter((p) => p.path.startsWith('/blog/') && p.meta.title && p.meta.date)
.sort((a, b) => new Date(b.meta.date) - new Date(a.meta.date))
return (
<Layout
environment={environment}
route="/blog"
title={generateTitle('Blog')}
>
<section css={contentStyle}>
<Heading
css={css`
text-transform: uppercase;
margin-top: 0;
`}
>
Blog
</Heading>
{articles.map((article) => (
<section
key={article.path}
css={css`
margin: 1em 0;
padding: 25px;
position: relative;
`}
>
<a
href={article.path}
css={css`
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
overflow: hidden;
text-indent: 200%;
white-space: nowrap;
pointer-events: auto;
z-index: 1;
transition: background-color 0.2s linear, box-shadow 0.2s linear;
&:hover {
transition: background-color 0.2s linear,
box-shadow 0.2s linear;
background: white;
box-shadow: 0 5px 15px rgba(0, 0, 0, 0.1);
&:active {
transition: box-shadow 0.07s linear;
box-shadow: 0 2px 5px rgba(0, 0, 0, 0.3);
}
}
`}
>
{article.meta.title}
</a>
<div
css={css`
position: relative;
pointer-events: none;
z-index: 2;
h2 {
font-size: 1.75em;
font-size: calc(1.25em + 0.4vw);
margin: 0 0 0.5em 0;
}
`}
>
<a href={article.path} tabIndex={-1}>
<h2>{article.meta.title}</h2>
</a>
<Meta
author={article.meta.author}
date={article.meta.date}
github={article.meta.github}
/>
{article.meta.description && (
<div css={blogStyle()} style={{ margin: '1em 0 0 0' }}>
<p style={{ margin: 0 }}>{article.meta.description}</p>
</div>
)}
</div>
</section>
))}
</section>
</Layout>
)
}

View File

@ -1,276 +0,0 @@
/** @jsx jsx */
import { jsx, css } from '@emotion/core'
import { MDXProvider } from '@mdx-js/react'
import GitHubSlugger from 'github-slugger'
import { Heading } from './components/blog/heading.js.jsx'
import { Layout, contentStyle, generateTitle, resolvePath } from './layout.jsx'
const articleStyle = css`
margin: 10px auto;
h1 {
margin: 0 0 0.5em 0;
}
`
const coverStyle = css`
box-shadow: 0 5px 15px rgba(0, 0, 0, 0.1);
display: block;
height: auto;
margin: 1em auto;
max-height: 320px;
max-width: 640px;
object-fit: cover;
width: 100%;
`
export const blogStyle = ({ fontSize = 17 } = {}) => css`
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen,
Ubuntu, Cantarell, 'Open Sans', 'Helvetica Neue', sans-serif,
Apple Color Emoji, Segoe UI Emoji;
font-size: ${fontSize}px;
letter-spacing: 0.03em;
line-height: 1.5;
margin: 2em 0;
white-space: break-word;
p {
margin: ${fontSize}px 0;
}
h1,
h2,
h3,
h4,
h5,
h6 {
margin: ${fontSize * 2}px 0 ${fontSize}px 0;
font-weight: bold;
}
h1 {
background: transparent
linear-gradient(to bottom, transparent 85%, #67b8e3 85%);
font-size: 1.6em;
max-width: 100%;
width: max-content;
}
h2 {
font-size: 1.3em;
}
h3 {
font-size: 1.1em;
}
h4 {
font-size: 1em;
}
h5 {
font-size: 0.9em;
}
h6 {
color: #666;
font-size: 0.8em;
}
img {
border-style: none;
max-width: 100%;
}
figure {
img {
display: block;
margin: 1em auto;
}
}
hr {
background: linear-gradient(-45deg, #eee 33%, #ccc 33%, #ccc 67%, #eee 67%)
repeat center center;
background-size: 6px 3px;
border: none;
height: 3px;
margin: ${fontSize * 2}px 0;
}
ul,
ol,
pre,
blockquote {
margin: ${fontSize * 1.5}px 0;
}
ul,
ol {
padding: 0 0 0 1.75em;
li {
margin: ${fontSize * 0.25}px 0;
}
ul,
ol {
margin: ${fontSize * 0.25}px 0;
}
}
code,
pre {
font-family: 'Source Code Pro', 'Courier New', Courier, monospace;
background-color: #f6f6f6;
border-radius: 2px;
letter-spacing: 0;
}
code {
font-size: 0.85em;
margin: 0;
padding: 0.15em 0.35em;
}
pre {
background-image: url('/assets/noise.png');
box-shadow: 0 1px 4px rgba(0, 0, 0, 0.15);
box-sizing: border-box;
font-weight: 500;
line-height: 1.1em;
overflow-x: auto;
white-space: pre;
word-wrap: normal;
> code {
background-color: transparent;
border-radius: 0;
box-shadow: none;
display: inline-block;
margin: 1em;
padding: 0;
}
}
blockquote {
border-left: 3px solid #007aad;
color: #666;
padding: 0 0 0 1em;
blockquote {
border-color: #009bda;
blockquote {
border-color: #78c5e9;
blockquote {
border-color: #ccc;
}
}
}
}
`
export const Meta = ({ author, date, github }) => (
<div
css={css`
font-size: calc(11.5px + 0.25vw);
font-weight: 500;
color: #666;
&,
& > a {
align-items: center;
display: flex;
flex-wrap: wrap;
}
img {
width: 2em;
height: 2em;
border-radius: 2em;
}
`}
>
<time dateTime={date}>Posted {date}</time>
{author && (
<>
&nbsp;by&nbsp;
{(() => {
if (!github) return author
return (
<a
href={`https://github.com/${github}`}
rel="noopener"
// eslint-disable-next-line react/jsx-no-target-blank
target="_blank"
>
<img
alt={author}
src={`https://github.com/${github}.png`}
width="40"
height="40"
/>
&nbsp;{author}
</a>
)
})()}
</>
)}
</div>
)
export const BlogLayout = ({ children, meta }) => {
const {
title,
description,
image,
imageCaption,
slug,
date,
author,
github,
} = meta
const route = slug && `/blog/${slug}`
// Slugified headings
const slugger = new GitHubSlugger()
const h1 = (props) => <Heading {...props} level={1} slugger={slugger} />
const h2 = (props) => <Heading {...props} level={2} slugger={slugger} />
const h3 = (props) => <Heading {...props} level={3} slugger={slugger} />
const h4 = (props) => <Heading {...props} level={4} slugger={slugger} />
const h5 = (props) => <Heading {...props} level={5} slugger={slugger} />
const h6 = (props) => <Heading {...props} level={6} slugger={slugger} />
return (
<Layout
description={description}
image={image}
route={route}
title={generateTitle('Blog', title)}
>
<article css={[contentStyle, articleStyle]}>
<a href={route || '#'}>
<h1 style={{ marginTop: 0 }}>{title}</h1>
</a>
<Meta author={author} date={date} github={github} />
{image && (
<figure>
<img
css={coverStyle}
src={resolvePath(image)}
alt={imageCaption || title}
/>
{imageCaption && <figcaption>{imageCaption}</figcaption>}
</figure>
)}
<div css={blogStyle()}>
<MDXProvider components={{ h1, h2, h3, h4, h5, h6 }}>
{children}
</MDXProvider>
</div>
</article>
</Layout>
)
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 19 KiB

View File

@ -1,63 +0,0 @@
/** @jsx jsx */
import { css, jsx } from '@emotion/core'
import GitHubSlugger from 'github-slugger'
import innerText from 'react-innertext'
export const Heading = ({
anchorOffset = -80,
children,
level = 1,
slugger = new GitHubSlugger(),
...props
}) => {
const anchor = slugger.slug(innerText(children))
const Tag = `h${level}`
return (
<>
<a
name={anchor}
css={css`
visibility: hidden;
display: block;
position: relative;
top: ${anchorOffset}px;
`}
/>
<Tag {...props}>
<span
css={css`
display: inline-block;
position: relative;
> .anchor-link {
align-items: center;
bottom: 0;
display: none;
justify-content: center;
left: -32px;
overflow: hidden;
position: absolute;
top: 0;
width: 32px;
}
&:hover > .anchor-link {
display: flex;
}
`}
>
<a href={`#${anchor}`} className="anchor-link" aria-hidden="true">
<img
src="https://icongr.am/octicons/link.svg?color=444455"
width="16"
height="16"
loading="lazy"
/>
</a>
{children}
</span>
</Tag>
</>
)
}

View File

@ -1,111 +0,0 @@
/** @jsx jsx */
import { css, jsx } from '@emotion/core'
const btnShadow = '0 3px 6px rgba(0, 0, 0, 0.25)'
const btnShadowFocus = '0 0 0 0.15em #67b8e3'
const button = css`
appearance: none;
border-radius: 1.5em;
border: 0;
box-shadow: ${btnShadow};
box-sizing: content-box;
cursor: pointer;
display: inline-block;
font: inherit;
letter-spacing: inherit;
line-height: inherit;
font-weight: bold;
outline: 0;
overflow: hidden;
margin: 0;
padding: 0.6em 0.95em;
text-decoration: none;
user-select: none;
white-space: nowrap;
&:hover {
transition: color 0.15s linear, background-color 0.15s linear;
&:active {
transition: none;
}
}
&:focus {
box-shadow: ${btnShadowFocus}, ${btnShadow};
}
`
const outline = css`
padding: 0.475em 0.825em;
`
const colors = {
default: {
common: css`
background-color: #fff;
color: #444;
&:hover {
color: #444;
background-color: #f8f8f8;
&:active {
color: #444;
background-color: #e0e0e0;
}
}
`,
outline: css`
border: 0.125em solid #444;
`,
},
primary: {
regular: css`
color: #fff;
background: #0288d1
linear-gradient(30deg, transparent, rgba(255, 255, 255, 0.3));
&:hover {
color: #fff;
background-color: #0277b7;
&:active {
color: #fff;
background-color: #02669d;
}
}
`,
outline: css`
color: #0288d1;
border: 0.125em solid #0288d1;
background-color: #fff;
&:hover {
color: #fff;
background-color: #0288d1;
&:active {
color: #fff;
background-color: #0277b7;
border-color: #0277b7;
}
}
`,
},
}
export const Button = (props) => {
const color = colors[props.color] || colors.default
const style = [
button,
color.common,
props.outline && outline,
color[props.outline ? 'outline' : 'regular'],
props.css,
]
if (props.href) return <a tabIndex={0} role="button" {...props} css={style} />
return <button {...props} css={style} />
}

View File

@ -1,60 +0,0 @@
/** @jsx jsx */
import { css, jsx } from '@emotion/core'
import highlightjs from 'highlight.js'
export const Code = ({ children, language, style }) => {
const colored = highlightjs.getLanguage(language)
? highlightjs.highlight(language, children, true)
: highlightjs.highlightAuto(children)
const lines = colored.value.split('\n')
const codeStyle = css`
--line-number-width: 3.5em;
cursor: text;
font-family: 'Source Code Pro', 'Courier New', Courier, monospace;
font-weight: 500;
letter-spacing: 0;
word-wrap: break-word;
> ol {
box-sizing: border-box;
list-style: none;
margin: 0;
padding: 0 0 0 var(--line-number-width);
}
.code-line {
counter-increment: code;
&::before {
pointer-events: none;
display: inline-block;
text-align: right;
content: counter(code);
margin-left: calc(var(--line-number-width) * -1);
width: var(--line-number-width);
box-sizing: border-box;
padding-right: calc(var(--line-number-width) * 0.3);
color: rgba(0, 0, 0, 0.5);
font-size: 80%;
}
}
`
return (
<div css={[codeStyle, style]}>
<ol>
{lines.map((line, i) => (
<li className="code-line" key={i}>
{line === '' ? (
<br />
) : (
<span dangerouslySetInnerHTML={{ __html: line }} />
)}
</li>
))}
</ol>
</div>
)
}

View File

@ -1,37 +0,0 @@
/** @jsx jsx */
import { css, jsx } from '@emotion/core'
export const Heading = ({ children, level = 1, ...props }) => {
const Tag = `h${level}`
return (
<span
css={css`
display: block;
filter: drop-shadow(0 5px 10px rgba(0, 0, 0, 0.15));
`}
>
<Tag
css={css`
background-color: #009bda;
background-image: url('/assets/noise.png'),
linear-gradient(-30deg, #02669d, #009bda);
box-sizing: border-box;
clip-path: polygon(1.5em 0, 100% 0, calc(100% - 1.5em) 100%, 0 100%);
color: #fff;
line-height: 1.2;
margin-left: auto;
margin-right: auto;
max-width: calc(100% - 60px);
padding: 0.25em 2em;
text-align: center;
width: max-content;
word-wrap: break-word;
`}
{...props}
>
{children}
</Tag>
</span>
)
}

View File

@ -1,47 +0,0 @@
/** @jsx jsx */
import { css, jsx } from '@emotion/core'
import { Marp as MarpCore } from '@marp-team/marp-core'
import { resolvePath } from '../layout.jsx'
const reset = css`
all: initial;
display: inline;
display: contents;
svg {
pointer-events: none;
user-select: none;
vertical-align: bottom;
}
`
const container = css`
display: inline-block;
`
export const Marp = (props) => {
const marp = new MarpCore({
container: null,
script: false,
printable: false,
})
marp.markdown.normalizeLink = (url) => resolvePath(url)
const rendered = marp.render(props.markdown, { htmlAsArray: true })
return (
<div css={reset}>
<div
css={[
container,
css([rendered.css.replace(/\/\*[\s\S]*?\*\//g, '')]),
props.style,
]}
dangerouslySetInnerHTML={{
__html: rendered.html[props.page ? props.page - 1 : 0],
}}
/>
</div>
)
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 680 B

View File

@ -1 +0,0 @@
@import 'highlight.js/styles/atom-one-light.css';

View File

@ -1,27 +0,0 @@
/** @jsx jsx */
import { css, jsx } from '@emotion/core'
import { Description } from './index/description.js.jsx'
import { Features } from './index/features.js.jsx'
import { GetStarted } from './index/get-started.js.jsx'
import { Hero } from './index/hero.js.jsx'
import { Layout } from './layout.jsx'
export default function Index() {
return (
<Layout
route="/"
description="Marp, Markdown Presentation Ecosystem, provides the great experience to create beautiful slide deck. You only have to focus writing your story in Markdown document."
type="website"
globalStyles={css`
html {
scroll-behavior: smooth;
}
`}
>
<Hero />
<Description />
<Features />
<GetStarted />
</Layout>
)
}

View File

@ -1,6 +0,0 @@
document
.getElementById('show-markdown-example')
.addEventListener('click', () => {
const example = document.getElementById('markdown-example')
example.open = !example.open
})

View File

@ -1,140 +0,0 @@
/** @jsx jsx */
import { css, jsx } from '@emotion/core'
import { version } from '@marp-team/marp-core/package.json'
import { Button } from '../components/button.js.jsx'
import { Code } from '../components/code.js.jsx'
import { Marp } from '../components/marp.js.jsx'
import { contentStyle, resolvePath } from '../layout.jsx'
const example = (resolver = (v) => v) =>
`
---
theme: gaia
_class: lead
paginate: true
backgroundColor: #fff
backgroundImage: url('${resolver('/assets/hero-background.jpg')}')
---
![bg left:40% 80%](https://raw.githubusercontent.com/marp-team/marp/master/marp.png)
# **Marp**
Markdown Presentation Ecosystem
https://marp.app/
---
# How to write slides
Split pages by horizontal ruler (\`---\`). It's very simple! :satisfied:
\`\`\`markdown
# Slide 1
foobar
---
# Slide 2
foobar
\`\`\`
`.trim()
const MarpExample = ({ page }) => (
<Marp
markdown={example()}
page={page}
style={css`
border: thin solid #ddd;
box-shadow: 0 5px 10px rgba(0, 0, 0, 0.15);
box-sizing: border-box;
margin: 20px;
max-width: 360px;
width: 80%;
`}
/>
)
export const Description = () => (
<section
css={[
contentStyle,
css`
font-weight: 500;
text-align: center;
> section {
text-align: left;
p {
margin-left: auto;
margin-right: auto;
max-width: 640px;
}
}
> figure {
margin-top: 1em;
}
`,
]}
>
<h1>
<mark>The great experience</mark> for creating slide deck
</h1>
<section>
<p>
Marp, Markdown Presentation Ecosystem, provides the great experience to
create beautiful slide deck. You only have to focus writing your story
in Markdown document.
</p>
</section>
<figure>
<MarpExample page={1} />
<MarpExample page={2} />
<figcaption>
We&apos;re rendering slides generated in{' '}
<a href="https://github.com/marp-team/marp-core">Marp Core</a>
</figcaption>
</figure>
<p>
<Button
onClick="this"
id="show-markdown-example"
style={{ fontSize: '0.85em' }}
>
Show Markdown example...
</Button>
</p>
<section>
<details id="markdown-example">
<summary style={{ display: 'none' }} />
<Code
language="markdown"
style={css`
border: thin solid #eee;
background: #f6f6f6 url('/assets/noise.png');
border-radius: 15px;
box-sizing: border-box;
font-size: 0.8em;
margin: 0 auto;
max-width: 800px;
padding: 20px 10px;
width: 85%;
`}
>
{example(resolvePath)}
</Code>
</details>
</section>
<script src="/index/description.js" />
<script
async
src={`https://cdn.jsdelivr.net/npm/@marp-team/marp-core@${version}/lib/browser.js`}
/>
</section>
)

View File

@ -1,373 +0,0 @@
/** @jsx jsx */
import { css, jsx } from '@emotion/core'
import { contentStyle } from '../layout.jsx'
const FeatureSections = (props) => (
<section {...props}>
<section>
<figure>
<img
src="https://icongr.am/octicons/markdown.svg?size=50&amp;color=444455"
alt="Based on CommonMark"
/>
</figure>
<h2>
Based on <mark>CommonMark</mark>
</h2>
<p>
If you know how to write document with Markdown, you already know how to
write Marp slide deck too. Our format is based on{' '}
<a
href="https://commonmark.org/"
target="_blank"
rel="noopener noreferrer"
>
CommonMark
</a>
, the consistent spec of Markdown. The only important difference is{' '}
<a
href="https://marpit.marp.app/markdown"
rel="noopener noreferrer"
target="_blank"
>
a ruler <code>---</code> for splitting pages.
</a>
</p>
</section>
<section>
<figure>
<img
src="https://icongr.am/octicons/code-square.svg?size=50&amp;color=444455"
alt="Directives and extended syntax"
/>
</figure>
<h2>
<mark>Directives</mark> and <mark>extended syntax</mark>
</h2>
<p>
Nevertheless, you may think the simple text content is lacking to
emphasize your voice. We are supporting to create beautiful slide
through{' '}
<a
href="https://marpit.marp.app/directives"
rel="noopener noreferrer"
target="_blank"
>
directives
</a>{' '}
and extended syntax (
<a
href="https://marpit.marp.app/image-syntax"
rel="noopener noreferrer"
target="_blank"
>
Image syntax
</a>
,{' '}
<a
href="https://github.com/marp-team/marp-core#math-typesetting"
rel="noopener"
// eslint-disable-next-line react/jsx-no-target-blank
target="_blank"
>
math typesetting
</a>
,{' '}
<a
href="https://github.com/marp-team/marp-core#auto-scaling-features"
rel="noopener"
// eslint-disable-next-line react/jsx-no-target-blank
target="_blank"
>
auto-scaling
</a>
, etc...).
</p>
</section>
<section>
<figure>
<img
src="https://icongr.am/octicons/paintbrush.svg?size=50&amp;color=444455"
alt="Built-in themes and CSS theming"
/>
</figure>
<h2>
<mark>Built-in themes</mark> and <mark>CSS theming</mark>
</h2>
<p>
<a
href="https://github.com/marp-team/marp-core/"
rel="noopener"
// eslint-disable-next-line react/jsx-no-target-blank
target="_blank"
>
Our core engine
</a>{' '}
has{' '}
<a
href="https://github.com/marp-team/marp-core/tree/master/themes"
rel="noopener"
// eslint-disable-next-line react/jsx-no-target-blank
target="_blank"
>
3 built-in themes called <code>default</code>, <code>gaia</code>, and{' '}
<code>uncover</code>
</a>
, to tell your story beautifully. If you are feeling unsatisfied to
design, Marp can{' '}
<a
href="https://marpit.marp.app/theme-css?id=tweak-style-through-markdown"
rel="noopener noreferrer"
target="_blank"
>
tweak style through Markdown
</a>
, or{' '}
<a
href="https://marpit.marp.app/theme-css"
rel="noopener noreferrer"
target="_blank"
>
create your own theme with plain CSS
</a>
.
</p>
</section>
<section>
<figure>
<img
src="https://icongr.am/octicons/file.svg?size=50&amp;color=444455"
alt="Export to HTML, PDF, and PowerPoint"
/>
</figure>
<h2>
Export to <mark>HTML, PDF, and PowerPoint</mark>
</h2>
<p>
Have you finished writing? Let&apos;s share the deck with a favorite
way! We can convert Markdown into HTML, what is more, PDF and PowerPoint
document directly! (Powered by{' '}
<a
href="https://www.google.com/chrome/"
rel="noopener noreferrer"
target="_blank"
>
Google Chrome
</a>{' '}
/{' '}
<a
href="https://www.chromium.org/Home"
rel="noopener noreferrer"
target="_blank"
>
Chromium
</a>
)
</p>
</section>
<section>
<figure>
<img
src="https://icongr.am/octicons/package.svg?size=50&amp;color=444455"
alt="Marp family: The official toolset"
/>
</figure>
<h2>
<mark>Marp family</mark>: The official toolset
</h2>
<p>
Marp family has the rich toolset to assist your work.{' '}
<a
href="https://marketplace.visualstudio.com/items?itemName=marp-team.marp-vscode"
rel="noopener noreferrer"
target="_blank"
>
<b>Marp for VS Code</b>
</a>{' '}
extension can preview editting Markdown and custom theme immediately.{' '}
<a
href="https://github.com/marp-team/marp-cli/"
rel="noopener"
// eslint-disable-next-line react/jsx-no-target-blank
target="_blank"
>
<b>Marp CLI</b>
</a>{' '}
allows to convert Markdown through CLI interface.{' '}
<a
href="https://web.marp.app/"
rel="noopener"
// eslint-disable-next-line react/jsx-no-target-blank
target="_blank"
>
Marp Web <i>(Tech demo)</i>
</a>{' '}
can render your deck in online.{' '}
<a
href="https://github.com/marp-team/marp/"
rel="noopener"
// eslint-disable-next-line react/jsx-no-target-blank
target="_blank"
>
...and more!
</a>
</p>
</section>
<section>
<figure>
<img
src="https://icongr.am/octicons/plug.svg?size=50&amp;color=444455"
alt="Pluggable architecture"
/>
</figure>
<h2>
<mark>Pluggable</mark> architecture
</h2>
<p>
As a matter of fact,{' '}
<em>Marp is essentially just a converter for Markdown.</em> Marp
ecosystem is built on{' '}
<a
href="https://marpit.marp.app"
rel="noopener noreferrer"
target="_blank"
>
<b>Marpit framework</b>
</a>
, the skinny framework for creating HTML + CSS slide deck. It has a
pluggable architecture and developer can{' '}
<a
href="https://marpit.marp.app/usage?id=extend-marpit-by-plugins"
rel="noopener noreferrer"
target="_blank"
>
extend features via plugin
</a>
.
</p>
</section>
<section>
<figure>
<img
src="https://icongr.am/octicons/heart-fill.svg?size=50&amp;color=444455"
alt="Fully open source"
/>
</figure>
<h2>
Fully <mark>open-source</mark>
</h2>
<p>
We are loving open source! All tools and related libraries by{' '}
<a
href="https://github.com/marp-team"
rel="noopener"
// eslint-disable-next-line react/jsx-no-target-blank
target="_blank"
>
Marp team
</a>{' '}
are MIT license.
</p>
</section>
</section>
)
export const Features = () => {
const { length } = FeatureSections().props.children
return (
<div
css={css`
position: relative;
&:before {
position: absolute;
display: block;
content: '';
top: 0;
right: 0;
bottom: 0;
left: 0;
background: linear-gradient(
-8deg,
rgba(120, 197, 233, 0),
rgba(120, 197, 233, 0) 50%,
rgba(120, 197, 233, 0.5)
);
z-index: 0;
clip-path: polygon(0 15vw, 100% 0, 100% 100%, 0 100%);
}
`}
>
<FeatureSections
css={[
contentStyle,
css`
position: relative;
z-index: 1;
display: grid;
grid-template-rows: repeat(${length + 1}, auto);
grid-template-columns: 1fr;
max-width: 1200px;
section {
background: #fff;
box-shadow: 0 5px 10px rgba(0, 0, 0, 0.15);
box-sizing: border-box;
font-size: 85%;
margin: 10px;
padding: 25px;
white-space: break-word;
grid-column: 1;
figure {
margin: 0;
height: 50px;
text-align: center;
img {
width: 50px;
height: 50px;
}
}
h2 {
text-align: center;
}
p {
font-size: 14px;
font-size: calc(14px + 0.02vw);
margin-bottom: 0;
}
}
@media (min-width: 768px) {
grid-template-columns: 1fr 1fr;
section {
margin: 20px;
&:nth-of-type(odd) {
grid-column: 1;
}
&:nth-of-type(even) {
grid-column: 2;
}
${[...Array(length)].map(
(_, i) => css`
&:nth-of-type(${i + 1}) {
grid-row: ${i + 1} / span 2;
}
`
)}
}
}
`,
]}
/>
</div>
)
}

View File

@ -1,366 +0,0 @@
/** @jsx jsx */
import { css, jsx } from '@emotion/core'
import { Button } from '../components/button.js.jsx'
import { Heading } from '../components/heading.js.jsx'
export const GetStarted = () => (
<section
id="get-started"
css={css`
position: relative;
/* Fix position for anchor link */
padding-top: 110px;
margin: -110px 0 -1px 0;
&::before {
background: #0288d1 url('/assets/noise.png');
bottom: 0;
clip-path: polygon(0 0, 100% 8vw, 100% 100%, 0 100%);
content: '';
display: block;
left: 0;
overflow: visible;
position: absolute;
right: 0;
top: calc(-12vw + 110px);
z-index: 0;
}
&::after {
clear: both;
content: '';
display: block;
height: 1px;
overflow: hidden;
}
> p,
> section {
margin: 40px;
max-width: 1000px;
position: relative;
z-index: 1;
}
> p {
color: #fff;
margin: 30px auto;
padding: 0 40px;
text-align: center;
}
> section {
background: #fff url('/assets/noise.png');
border-radius: 1em;
box-shadow: 0 5px 10px rgba(0, 0, 0, 0.15);
box-sizing: border-box;
display: flex;
flex-direction: column;
align-items: center;
font-size: calc(16px + 0.1vw);
padding: 40px 30px;
white-space: break-word;
width: auto;
@media (min-width: 1060px) {
margin: 40px auto;
}
figure {
margin: 0 0 20px 0;
max-width: 75%;
min-width: 180px;
box-sizing: border-box;
}
@media (min-width: 768px) {
flex-direction: row;
figure {
max-width: 33%;
order: 2;
margin: 0 0 0 20px;
}
}
header {
margin: 0 0 1em 0;
h3 {
font-size: 1.5em;
margin: 0;
}
p {
color: #888;
font-size: 75%;
margin: 0.5em 0;
}
}
p {
&:last-child {
margin-bottom: 0;
}
}
[role='button'] {
margin-top: 8px;
font-size: 83%;
}
}
`}
>
<Heading
level={2}
css={css`
background: #02669d;
color: #fff;
margin-top: 10px;
margin-bottom: 10px;
`}
>
Tools and integrations
</Heading>
<section>
<figure>
<a
href="https://marketplace.visualstudio.com/items?itemName=marp-team.marp-vscode"
target="_blank"
rel="noopener noreferrer"
>
<img
src="/assets/marp-for-vs-code.png"
alt="Marp for VS Code"
loading="lazy"
/>
</a>
</figure>
<div>
<header>
<h3>
<mark>Marp for VS Code</mark>{' '}
<img
src="https://img.shields.io/visual-studio-marketplace/v/marp-team.marp-vscode.svg?style=flat-square&amp;label=&amp;colorB=67b8e3"
alt="Marp for VS Code"
/>
</h3>
<p>Create slide deck written in Marp Markdown on VS Code</p>
</header>
<p>
Enhance VS Code&apos;s Markdown preview pane to support writing your
beautiful presentation. You can see the slide deck output as soon as
editting Markdown.
</p>
<p>
<Button
href="https://marketplace.visualstudio.com/items?itemName=marp-team.marp-vscode"
target="_blank"
rel="noopener noreferrer"
color="primary"
>
VS Marketplace
</Button>{' '}
<Button
href="https://github.com/marp-team/marp-vscode"
rel="noopener"
target="_blank"
outline="true"
>
GitHub
</Button>
</p>
</div>
</section>
<section>
<figure>
<a
href="https://github.com/marp-team/marp-cli"
// eslint-disable-next-line react/jsx-no-target-blank
target="_blank"
rel="noopener"
>
<img src="/assets/marp-cli.png" alt="Marp CLI" loading="lazy" />
</a>
</figure>
<div>
<header>
<h3>
<mark>Marp CLI</mark>{' '}
<img
src="https://img.shields.io/npm/v/@marp-team/marp-cli.svg?style=flat-square&amp;label=&amp;colorB=67b8e3"
alt="Marp CLI"
/>
</h3>
<p>A CLI interface for Marp and Marpit based converters</p>
</header>
<p>
CLI is the swiss army knife for Marp ecosystem. Convert your Markdown
into various formats, watch changes, launch server for on-demand
conversion, and customize engine.
</p>
<p>
<Button
href="https://www.npmjs.com/package/@marp-team/marp-cli"
rel="noopener noreferrer"
target="_blank"
color="primary"
outline="true"
>
npm
</Button>{' '}
<Button
href="https://github.com/marp-team/marp-cli"
rel="noopener"
target="_blank"
outline="true"
>
GitHub
</Button>
</p>
</div>
</section>
<Heading
level={3}
css={css`
background: #02669d;
color: #fff;
`}
>
For developers
</Heading>
<section>
<figure
css={css`
min-width: 100px !important;
max-width: 100px !important;
`}
>
<a
href="https://github.com/marp-team/marp-core"
// eslint-disable-next-line react/jsx-no-target-blank
target="_blank"
rel="noopener"
>
<img src="/assets/marp-logo.svg" alt="Marp Core" loading="lazy" />
</a>
</figure>
<div>
<header>
<h3>
<mark>Marp Core</mark>{' '}
<img
src="https://img.shields.io/npm/v/@marp-team/marp-core.svg?style=flat-square&amp;label=&amp;colorB=67b8e3"
alt="Marp Core"
/>
</h3>
<p>The core of Marp converter</p>
</header>
<p>
All official Marp tools provided by us are using this core as the
engine. It is based on Marpit framework, and includes some extended
features to help creating beautiful slide deck.
</p>
<p>
<Button
href="https://www.npmjs.com/package/@marp-team/marp-core"
target="_blank"
rel="noopener noreferrer"
color="primary"
outline="true"
>
npm
</Button>{' '}
<Button
href="https://github.com/marp-team/marp-core"
rel="noopener"
target="_blank"
outline="true"
>
GitHub
</Button>
</p>
</div>
</section>
<section>
<figure
css={css`
min-width: 100px !important;
max-width: 100px !important;
`}
>
<a
href="https://marpit.marp.app/"
// eslint-disable-next-line react/jsx-no-target-blank
target="_blank"
rel="noopener"
>
<img src="/assets/marpit.svg" alt="Marpit" loading="lazy" />
</a>
</figure>
<div>
<header>
<h3>
<mark>Marpit</mark> framework{' '}
<img
src="https://img.shields.io/npm/v/@marp-team/marpit.svg?style=flat-square&amp;label=&amp;colorB=67b8e3"
alt="Marpit framework"
/>
</h3>
<p>The skinny framework for creating slide deck from Markdown</p>
</header>
<p>
Marpit, independented from Marp, is the skinny framework to transform
Markdown + CSS theme to the deck composed of HTML + CSS. It has
designed to output only minimum assets.
</p>
<p>
<Button
href="https://marpit.marp.app/"
target="_blank"
rel="noopener noreferrer"
color="primary"
>
Documentation
</Button>{' '}
<Button
href="https://www.npmjs.com/package/@marp-team/marpit"
target="_blank"
rel="noopener noreferrer"
color="primary"
outline="true"
>
npm
</Button>{' '}
<Button
href="https://github.com/marp-team/marpit"
rel="noopener"
target="_blank"
outline="true"
>
GitHub
</Button>
</p>
</div>
</section>
<p>
...and find out all tools, integrations, examples at our GitHub entrance
repository!
</p>
<p>
<Button
color="primary"
outline="true"
href="https://github.com/marp-team/marp/"
rel="noopener"
target="_blank"
css={css`
font-size: calc(12px + 0.25vw);
`}
>
Go to the entrance repository...
</Button>
</p>
</section>
)

View File

@ -1,66 +0,0 @@
/** @jsx jsx */
import { css, jsx } from '@emotion/core'
import { Button } from '../components/button.js.jsx'
import { defaultTitle } from '../layout.jsx'
export const Hero = () => (
<section
css={css`
background: #fcfcfc url('./assets/hero-background.jpg') no-repeat right
center;
background-size: cover;
overflow: hidden;
padding: 70px 20px;
text-align: center;
`}
>
<h1
css={css`
margin: 50px 0;
font-size: calc(12px + 0.75vw);
font-weight: bold;
letter-spacing: 1px;
> img {
display: block;
max-width: 560px;
width: 80%;
height: auto;
margin: 0 auto 20px auto;
}
`}
>
<img
src="https://raw.githubusercontent.com/marp-team/marp/master/marp.png"
alt={defaultTitle}
/>
Markdown Presentation Ecosystem
</h1>
<p>
{/* TODO: Change this link to "Docs" page that would be hosted on marp.app in future */}
<Button
color="primary"
href="#get-started"
css={css`
font-size: calc(18px + 0.5vw);
padding: 0.75em 1.5em;
`}
>
Get started!
</Button>
</p>
<p>
<Button
color="primary"
outline="true"
href="https://github.com/marp-team/marp/"
rel="noopener"
css={css`
font-size: calc(12px + 0.25vw);
`}
>
Find out Marp tools at GitHub...
</Button>
</p>
</section>
)

View File

@ -1,425 +0,0 @@
/** @jsx jsx */
import { URL } from 'url'
import { Global, css, jsx } from '@emotion/core'
export const defaultTitle = 'Marp: Markdown Presentation Ecosystem'
export const defaultImage = '/assets/og-image.png'
export const generateTitle = (...breadcrumbs) =>
['Marp', ...breadcrumbs].reverse().join(' | ')
export const resolvePath = (path) =>
new URL(
path,
(() => {
// For Netlify deploy preview
if (process.env.CONTEXT === 'deploy-preview')
return process.env.DEPLOY_URL
if (process.env.NODE_ENV === 'production') return 'https://marp.app/'
return 'http://localhost:2468/'
})()
).toString()
export const contentStyle = css`
max-width: 1000px;
overflow: hidden;
padding: 30px;
margin: 0 auto;
`
const globalStyle = css`
html,
body {
padding: 0;
margin: 0;
}
html {
height: 100%;
}
body {
color: #445;
font-family: Quicksand, Avenir, Century Gothic, -apple-system,
BlinkMacSystemFont, sans-serif, Apple Color Emoji, Segoe UI Emoji;
font-size: 18px;
font-size: calc(15px + 0.2vw);
letter-spacing: 0.04em;
line-height: 1.4;
min-height: 100%;
position: relative;
/* "background-attachment: fixed" may break background rendering in mobile device. */
&::before {
display: block;
position: fixed;
z-index: -1;
top: 0;
left: 0;
width: 100%;
height: 100%;
content: '';
background-color: #f8f8f8;
background-image: url('/assets/noise.png'),
linear-gradient(to bottom, #fafafa, #fff 50%);
}
}
img {
border: 0;
}
a {
color: #0288d1;
text-decoration: none;
&:hover {
color: #02669d;
transition: color 0.15s linear;
&:active {
color: #1b4d68;
transition: none;
}
}
}
h1 {
font-size: 1.75em;
font-size: calc(1.25em + 0.4vw);
}
h2 {
font-size: 1.6em;
font-size: calc(1.2em + 0.2vw);
}
h3 {
font-size: 1.35em;
font-size: calc(1.13em + 0.1vw);
}
mark {
color: inherit;
background-color: transparent;
background-image: linear-gradient(to bottom, transparent 85%, #67b8e3 85%);
}
figure {
margin: 2em 0;
img {
max-width: 100%;
}
figcaption {
color: #999;
text-align: center;
font-size: 75%;
}
}
code {
font-family: 'Source Code Pro', 'Courier New', Courier, monospace;
background-color: #f8f8f8;
letter-spacing: 0;
padding: 0 0.2em;
}
`
const headerHeight = 80
const Header = ({ route }) => (
<header
css={css`
--header-gap: calc(5px + 1vw);
background: #fff;
border: 0;
border-bottom-width: thin;
border-color: rgba(51, 51, 51, 0.07);
border-style: solid;
box-shadow: 0 0 40px rgba(128, 128, 128, 0.05);
box-sizing: border-box;
display: flex;
font-size: ${headerHeight * 0.22}px;
height: ${headerHeight}px;
left: 0;
overflow: hidden;
padding: 0 var(--header-gap);
position: sticky;
justify-content: center;
top: 0;
width: 100%;
z-index: 99;
> a {
align-items: center;
align-self: center;
display: flex;
margin: 0 var(--header-gap);
width: ${headerHeight * 0.75}px;
> img {
width: ${headerHeight * 0.75}px;
height: ${headerHeight * 0.75}px;
}
}
> nav {
> ul {
align-items: center;
display: flex;
font-weight: 500;
height: 100%;
letter-spacing: 0.1vw;
list-style: none;
margin: 0;
padding: 0;
text-transform: uppercase;
a {
position: relative;
align-items: center;
color: currentColor;
display: flex;
margin: 0 var(--header-gap);
text-decoration: none;
&::before {
/* Expand hit area */
content: '';
display: block;
position: absolute;
left: calc(var(--header-gap) * -1);
right: calc(var(--header-gap) * -1);
top: ${headerHeight * -0.5}px;
bottom: ${headerHeight * -0.5}px;
}
&::after {
content: '';
display: block;
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: ${headerHeight * -0.075}px;
transition: box-shadow 0.2s linear;
}
&:hover {
&::after {
box-shadow: inset 0 ${headerHeight * -0.05}px
rgba(0, 0, 0, 0.25);
}
}
&.active {
&::after {
transition: none;
box-shadow: inset 0 ${headerHeight * -0.05}px #009bda;
}
}
&:focus {
outline: 0;
&::after {
transition: none;
box-shadow: inset 0 ${headerHeight * -0.05}px #78c5e9;
}
}
&:hover:active {
&::after {
transition: none;
box-shadow: inset 0 ${headerHeight * -0.05}px #007aad;
}
}
}
}
}
`}
>
<a href="/">
<img src="/assets/marp-logo.svg" alt={defaultTitle} />
</a>
<nav>
<ul>
<li>
<a
href="/blog"
className={
route && route.startsWith('/blog') ? 'active' : undefined
}
>
Blog
</a>
</li>
<li>
<a
href="https://github.com/marp-team/marp"
// eslint-disable-next-line react/jsx-no-target-blank
target="_blank"
rel="noopener"
>
GitHub
</a>
</li>
</ul>
</nav>
</header>
)
const footerHeight = 90
const Footer = ({ children }) => (
<footer
css={css`
background: #334 url('/assets/noise.png');
box-sizing: border-box;
color: #bbc;
min-height: ${footerHeight}px;
padding: 10px 30px 30px 10px;
overflow: hidden;
display: flex;
flex-direction: row;
flex-wrap: wrap;
align-items: center;
p {
margin: 20px 0 0 20px;
}
iframe {
vertical-align: bottom;
}
`}
>
{children}
</footer>
)
export const Layout = ({
children,
description,
globalStyles,
image = defaultImage,
route,
title = defaultTitle,
type = 'article',
}) => (
<html lang="en">
<head>
<title>{title}</title>
<meta charSet="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<meta httpEquiv="X-UA-Compatible" content="ie=edge" />
{description && (
<>
<meta name="description" content={description} />
<meta property="og:description" content={description} />
</>
)}
{route && (
<>
<link rel="canonical" content={resolvePath(route)} />
<meta property="og:url" content={resolvePath(route)} />
</>
)}
<meta property="og:title" content={title} />
<meta property="og:type" content={type} />
<meta property="og:image" content={resolvePath(image)} />
<meta
property="twitter:card"
content={type === 'website' ? 'summary_large_image' : 'summary'}
/>
<link rel="icon" href="/favicon.png" type="image/png" />
<link
rel="apple-touch-icon"
sizes="180x180"
href="/apple-touch-icon-180x180.png"
/>
<link
rel="stylesheet"
href="https://fonts.googleapis.com/css?family=Quicksand:400,500,700|Source+Code+Pro:400,500&amp;display=swap"
/>
<link href="/highlightjs.css" rel="stylesheet" />
<Global styles={[globalStyle, globalStyles]} />
</head>
<body>
<Header route={route} />
<main
css={css`
min-height: calc(100vh - ${headerHeight + footerHeight}px);
`}
>
{children}
</main>
<Footer>
<p>Copyright &copy; 2019- Marp Team.</p>
<p>
<iframe
src="https://ghbtns.com/github-btn.html?user=marp-team&amp;repo=marp&amp;type=star&amp;count=true"
title="Stars"
loading="lazy"
frameBorder="0"
scrolling="0"
width="160"
height="20"
/>
</p>
</Footer>
<a
href="#"
title="Back to top"
css={css`
position: fixed;
right: 0;
bottom: 0;
width: calc(30px + 5vw);
height: calc(30px + 5vw);
max-width: ${footerHeight}px;
max-height: ${footerHeight}px;
color: #fff !important;
z-index: 100;
display: block;
filter: drop-shadow(0 0 5px rgba(0, 0, 0, 0.25));
line-height: ${footerHeight * 3}px;
pointer-events: none;
overflow: hidden;
background: linear-gradient(135deg, transparent 50%, #67b8e3 50%);
&:hover {
background: linear-gradient(135deg, transparent 50%, #0288d1 50%);
&:active {
background: linear-gradient(135deg, transparent 50%, #02669d 50%);
}
}
&::after {
display: block;
position: absolute;
top: 0;
right: 0;
bottom: 0;
left: 0;
content: '';
pointer-events: auto;
clip-path: polygon(0 100%, 100% 0, 100% 100%);
background: url('https://icongr.am/octicons/arrow-up.svg?color=ffffff')
no-repeat 80% 80%;
background-size: 35% 35%;
}
`}
>
Back to top
</a>
</body>
</html>
)

9
tsconfig.json Normal file
View File

@ -0,0 +1,9 @@
{
"extends": "@tsconfig/recommended/tsconfig.json",
"compilerOptions": {
"lib": ["es2015", "dom"],
"noImplicitAny": false,
"resolveJsonModule": true,
"strict": true
}
}

1
website/.env.development Normal file
View File

@ -0,0 +1 @@
NEXT_PUBLIC_HOST=http://localhost:3000/

1
website/.env.production Normal file
View File

@ -0,0 +1 @@
NEXT_PUBLIC_HOST=https://marp.app/

29
website/.eslintrc.js Normal file
View File

@ -0,0 +1,29 @@
module.exports = {
extends: [
'plugin:import/react',
'plugin:react/recommended',
'plugin:react-hooks/recommended',
'plugin:jsx-a11y/recommended',
'prettier/react',
],
rules: {
'react/react-in-jsx-scope': 'off',
'jsx-a11y/anchor-is-valid': [
'error',
{
components: ['Link'],
specialLink: ['hrefLeft', 'hrefRight'],
aspects: ['invalidHref', 'preferButton'],
},
],
},
overrides: [
{
files: ['**/*.ts', '**/*.tsx'],
rules: {
'react/prop-types': 'off',
},
},
],
settings: { react: { version: 'detect' } },
}

19
website/babel.config.js Normal file
View File

@ -0,0 +1,19 @@
const path = require('path')
module.exports = {
presets: [
[
'next/babel',
{
'styled-jsx': {
plugins: [
[
'styled-jsx-plugin-postcss',
{ path: path.resolve(__dirname, './postcss.config.js') },
],
],
},
},
],
],
}

View File

@ -0,0 +1,44 @@
---
title: Re-creation of Marp website for the unified docs
date: 2020-08-22
author: Yuki Hattori
github: yhatt
---
[marpit framework]: https://marpit.marp.app/
[marp core]: https://github.com/marp-team/marp-core
[marp cli]: https://github.com/marp-team/marp-cli
[marp for vs code]: https://marketplace.visualstudio.com/items?itemName=marp-team.marp-vscode
I could not have imagined that now we are living in a unique pandemic era when I wrote [the last article](/blog/the-story-of-marp-next). Even under those severe circumstances, I'm still making progress of Marp.
Marp gives some tools for making a convincing slide deck with fewer efforts, and they get loved by a lot of users. In early this year, [Marp Core] has reached the stable v1 release, and our tools around the core are keeping steps with it. Needless to mention here, we will keep going to enhance our tools.
Today we are announcing that **Marp team is working to the re-creation of [marp.app](/) website for hosting the unified documentation**. If you are reading this article, you should have already seen the re-created website! Currently the unified docs is not yet ready but we are going to announce here as soon as getting ready.
<!-- more -->
# For the unified documentation
As Marp ecosystem spreads out, Marp team has become to regard the lack of unified documentation as an important issue. Our docs are scattered to many repos per tool, and it would make confusion when learning overall of Marp. In addition, we often have been asked basics of Marp in the issue tracker and sometimes even prevent our works for evolving Marp.
For making users take advantage of Marp easier, I'm going to work improving the documentation together with evolving Marp tools.
## Re-created [marp.app](/)
The re-created web page is the first step for building the unified docs. I had tried various tools to build the website and found a place to rest in [Next.js](https://nextjs.org/) and [Tailwind CSS](https://tailwindcss.com/). I believe we will be able to build more useful documentation pages by these.
It is managed in our entrance repository [marp-team/marp](https://github.com/marp-team/marp) as same as before. If shipped new documentation, we would accept some improvements in the documentation from the community.
---
## Mid-term plans for Marp tools
Might as well, finally let me share some mid-term plans for each tools I'll work shortly.
- [Marpit framework]: Enhance directives
- [Marp Core]: Add new built-in theme and simplify auto-scaling feature
- [Marp CLI]: Handout template
- [Marp for VS Code]: Better auto-completion for Marp directives
We also had announced [long-term plans earlier](/blog/the-story-of-marp-next): [Marp Web](https://web.marp.app/), Marp integration modules with [React](https://github.com/marp-team/marp-react) and [Vue](https://github.com/marp-team/marp-react), and [Marpit v2](https://github.com/marp-team/marpit/issues/194). However, _they are not yet in active and may need to reconsider plans because we have not enough positive feedbacks from community._

View File

@ -1,21 +1,11 @@
import { BlogLayout } from '../blog.jsx'
export const meta = {
slug: 'the-story-of-marp-next',
title: 'The story of Marp Next',
image: '/blog/og-images/the-story-of-marp-next.png',
description:
"Today, I'm so excited to introduce the story of Marp Next! The full-rewritten Marp is not only just a writer. To be usable in various situations, we build a brand-new Marp ecosystem consisted of multiple modules.",
date: '2019-06-06',
author: 'Yuki Hattori',
github: 'yhatt',
}
export const layout = ({ children, environment }) => (
<BlogLayout environment={environment} meta={meta}>
{children}
</BlogLayout>
)
---
title: The story of Marp Next
date: 2019-06-06
description: Today, I'm so excited to introduce the story of Marp Next! The full-rewritten Marp is not only just a writer. To be usable in various situations, we build a brand-new Marp ecosystem consisted of multiple modules.
author: Yuki Hattori
github: yhatt
image: /og-images/the-story-of-marp-next.png
---
The first version of [Marp](https://yhatt.github.io/marp/) was released at almost 3 years ago. At first, it was started from a simple tool for personal usage called "mdSlide". And now, Marp has been used by a lot of users who would recognize the real value of the presentation writer. Marp is amassed around [8,000 stars](https://github.com/yhatt/marp/stargazers) until now.
@ -23,6 +13,8 @@ However, our headache brought from lacked maintainability to develop. We had rec
Today, I'm so excited to introduce the story of Marp Next! The full-rewritten Marp is not only just a writer. To be usable in various situations, we build **a brand-new Marp ecosystem** consisted of multiple modules. They are developed with JavaScript and TypeScript, and much more maintainable than the previous Marp.
<!-- more -->
# Marp ecosystem
Marp Next has two core components: **[Marpit]** framework and **[Marp Core]**. Tools by Marp ecosystem are usually based on these.
@ -94,11 +86,7 @@ Many of the features are based on the old desktop app, and have improved to be s
**[Marp CLI]** is a CLI interface of Marpit and Marp Core converter. It's a Swiss-Army knife for Marp slide deck!
<figure>
[![](https://raw.githubusercontent.com/marp-team/marp-cli/master/docs/images/marp-cli.gif)][marp cli]
</figure>
[![Marp CLI](https://raw.githubusercontent.com/marp-team/marp-cli/master/docs/images/marp-cli.gif ' ')][marp cli]
You can use it right now by running `npx @marp-team/marp-cli` if [Node.js](https://nodejs.org/) is installed.
@ -131,22 +119,13 @@ It made [some strong oppositions by users that is using Marp in offline](https:/
And 2 years later, the time has come to use PWA! After the first access to **[https://web.marp.app/][marp web]**, Marp Web would be ready to use in both of online and offline. Online resources to use the web interface would be cached in your browser, and use them when network is offline.
<figure>
[<img src="https://raw.githubusercontent.com/marp-team/marp-web/master/desktop-pwa.png" width="640" />][marp web]
</figure>
[![Marp Web + Progressive Web Apps](https://raw.githubusercontent.com/marp-team/marp-web/master/desktop-pwa.png ' ')][marp web]
### Use via any devices
By migrating to the web-based app, Marp will be able using in mobile device: Android and iOS. That's sure it's well suited to the tablet device like iPad.
<figure>
<img
src="https://user-images.githubusercontent.com/3993388/50569518-5305c800-0daa-11e9-8fa4-08053c9b51cd.png"
width="640"
/>
</figure>
![Marp Web on iPad](https://user-images.githubusercontent.com/3993388/50569518-5305c800-0daa-11e9-8fa4-08053c9b51cd.png ' ')
Marp Web would work also in Chrome OS well. Marp especially has many users in the field of education, and supporting Chrome OS that has large share in its field is meaningful.
@ -164,11 +143,7 @@ The modulized Marp Core brought Marp integrations for some tools.
Honestly, I don't think to want to make a new editor because there are many great Markdown editors in the world. I had been thinking it would be awesome if Marp could integrate with a something else powerful Markdown editor. And now, Marp can use in [Visual Studio Code](https://code.visualstudio.com/)!
<figure>
[<img src="https://marp.app/assets/marp-for-vs-code.png" width="640" />][marp vscode]
</figure>
![Marp for VS Code](/assets/marp-for-vs-code.png ' ')
It was realized because VS Code is using the same Markdown engine (markdown-it) as Marpit framework. Of course you can export slides as PDF and HTML easily, powered by [Marp CLI].
@ -234,4 +209,4 @@ Marp Next just focuses to build the ecosystem for Markdown slide deck with pure
We still have stood at the beginning of the brand-new ecosystem. Are you interested to Marp team and our ecosystem? We welcome to start your contribution! See [our contributing guideline](https://github.com/marp-team/marp/blob/master/.github/CONTRIBUTING.md) and get started!
> PS. I've started [Patreon](https://www.patreon.com/yhatt) and stood in a line of [GitHub Sponsors](https://github.com/sponsors). These are also good contribution if you want to help my working for open source.
> PS. [GitHub Sponsors](https://github.com/sponsors/yhatt) is also good contribution if you want to help my working for open source.

View File

@ -0,0 +1,104 @@
import classNames from 'classnames'
export type ButtonProps = {
color?: 'primary'
outline?: boolean
[key: string]: unknown
}
export const Button = ({
children,
className,
color,
href,
outline,
...rest
}: ButtonProps) => {
const Tag = href ? 'a' : 'button'
const attrs = {
...rest,
...(Tag === 'a' ? { href, role: 'button', tabIndex: 0 } : {}),
}
return (
<Tag
{...attrs}
className={classNames(
Tag === 'a' && 'custom-anchor',
'button',
color,
{ outline },
className as any
)}
>
{children}
<style jsx>{`
.button {
@apply appearance-none no-underline inline-block relative select-none font-bold rounded-full shadow-md bg-white text-center;
padding: 0.625em 1.25em;
transition: color, background-color, opacity;
}
@screen md {
.button {
@apply tracking-wider;
}
}
.button:hover {
@apply bg-background duration-150;
}
.button:hover:active {
@apply outline-none shadow-outline bg-gray-300;
transition-duration: 0s;
}
.button:focus {
@apply outline-none shadow-outline;
}
/* Primary color */
.button.primary {
@apply bg-marp-brand text-white;
background-image: linear-gradient(
30deg,
transparent,
rgba(255, 255, 255, 0.3)
);
}
.button.primary:hover {
@apply bg-marp-darken;
}
.button.primary:hover:active {
@apply bg-marp-dark;
}
/* Outline */
.button.outline {
@apply text-foreground;
}
.button.outline::after {
@apply absolute block inset-0 pointer-events-none border-current border-2;
border-radius: inherit;
content: '';
transition: inherit;
}
.button.outline.primary {
@apply text-marp-brand bg-white;
background-image: none;
}
.button.outline.primary:hover {
@apply bg-marp-darken text-white;
}
.button.outline.primary:hover::after {
@apply opacity-0;
}
`}</style>
</Tag>
)
}

View File

@ -0,0 +1,176 @@
/* eslint-disable react/jsx-key */
import classNames from 'classnames'
import Highlight, {
defaultProps,
Language,
PrismTheme,
} from 'prism-react-renderer'
import nightOwlLight from 'prism-react-renderer/themes/nightOwlLight'
import { useRef, useState, MouseEvent } from 'react'
import { Button } from 'components/Button'
const theme: PrismTheme = {
plain: {
...nightOwlLight.plain,
backgroundColor: '#f5f5f5',
},
styles: [
...nightOwlLight.styles,
{ types: ['italic'], style: { fontStyle: 'italic' } },
{ types: ['important', 'bold'], style: { fontWeight: 'bold' } },
],
}
export type CodeBlockProps = {
children: string
copyButton?: boolean
language: Language
lineNumber?: boolean
[key: string]: unknown
}
export const CodeBlock = ({
children,
className,
copyButton,
language,
lineNumber = false,
...rest
}: CodeBlockProps) => {
const [copied, setCopied] = useState(false)
const copiedTimer = useRef<number | undefined>(undefined)
return (
<>
<Highlight
{...defaultProps}
code={children}
language={language}
theme={theme}
>
{({ className: cn, style, tokens, getLineProps, getTokenProps }) => (
<div className={classNames('code-block-container', className as any)}>
<pre
className={classNames(lineNumber && 'line-number', cn)}
style={style}
{...rest}
>
<code className="code-block">
<ol className="code-block">
{tokens.map((line, i) => {
const lineProps = getLineProps({ line, key: i })
return (
<li
{...lineProps}
className={classNames(
lineProps.className,
'code-block'
)}
>
{line.map((token, key) =>
token.empty ? (
<br key={key} />
) : (
<span {...getTokenProps({ token, key })} />
)
)}
</li>
)
})}
</ol>
</code>
</pre>
{copyButton && (
<div className="copy-btn-container">
<Button
className={copied ? 'copied' : undefined}
onClick={(e: MouseEvent<HTMLButtonElement>) => {
const tmpTextarea = document.createElement('textarea')
tmpTextarea.value = children
tmpTextarea.style.position = 'absolute'
tmpTextarea.style.left = '0'
tmpTextarea.style.top = '0'
tmpTextarea.style.opacity = '0'
tmpTextarea.style.pointerEvents = 'none'
document.body.appendChild(tmpTextarea)
tmpTextarea.select()
document.execCommand('copy')
document.body.removeChild(tmpTextarea)
e.currentTarget.focus()
// Update React state
setCopied(true)
if (copiedTimer.current !== undefined) {
window.clearTimeout(copiedTimer.current)
}
copiedTimer.current = window.setTimeout(() => {
copiedTimer.current = undefined
setCopied(false)
}, 1000)
}}
>
{copied ? 'Copied!' : 'Copy'}
</Button>
</div>
)}
</div>
)}
</Highlight>
<style jsx>{`
.code-block-container {
@apply relative;
}
.prism-code {
@apply text-sm border rounded-lg leading-5 whitespace-pre overflow-x-auto overflow-y-hidden break-words shadow-inner;
font-family: inherit;
background-image: url('/assets/noise.png');
}
.prism-code code {
@apply inline-block p-4 min-w-full font-mono;
}
.prism-code.line-number {
@apply whitespace-pre-wrap;
}
.prism-code.line-number ol {
counter-reset: line 0;
}
.prism-code.line-number li {
@apply relative pl-12;
counter-increment: line;
}
.prism-code.line-number li::before {
@apply absolute inset-0 w-12 pr-3 text-right text-gray-500 text-xs leading-5;
content: counter(line);
}
.copy-btn-container {
@apply absolute top-0 right-0 m-3;
}
.copy-btn-container :global(button) {
@apply text-xs opacity-0 transition-opacity duration-300 w-24 py-1;
}
.code-block-container:hover .copy-btn-container :global(button),
.copy-btn-container :global(button):focus {
@apply opacity-100;
}
`}</style>
</>
)
}

View File

@ -0,0 +1,29 @@
import { ScrollToTop } from 'components/ScrollToTop'
export const Footer = () => (
<footer>
<div className="container mx-auto table">
<p className="mx-6 my-5 mr-20 leading-loose">
Copyright © 2019-{process.env.BUILD_YEAR} Marp team.&emsp;
<iframe
className="inline-block align-text-top"
src="https://ghbtns.com/github-btn.html?user=marp-team&amp;repo=marp&amp;type=star&amp;count=true"
frameBorder={0}
scrolling="0"
width={150}
height={20}
title="GitHub"
></iframe>
</p>
<ScrollToTop />
</div>
<style jsx>{`
footer {
@apply bg-gray-800 text-gray-500;
min-height: 4.5rem;
background-image: url('/assets/noise.png');
}
`}</style>
</footer>
)

View File

@ -0,0 +1,147 @@
import classNames from 'classnames'
import Head from 'next/head'
import Link from 'next/link'
const handleMouseUp = (e: React.MouseEvent<HTMLElement>) =>
e.currentTarget.blur()
export type ItemSlug = 'docs' | 'blog'
export const Header = ({ activeItem }: { activeItem?: ItemSlug }) => (
<>
<Head>
<link rel="preload" href="/assets/marp-logo.svg" as="image" />
</Head>
<header className="z-50 flex justify-center bg-white shadow-sm fixed top-0 left-0 w-full h-16 md:h-20">
<Link href="/">
<a
className="custom-anchor header-item"
role="link"
tabIndex={0}
onMouseUp={handleMouseUp}
>
<img
src="/assets/marp-logo.svg"
alt="Marp"
className="block h-16 w-16 p-2 md:p-3 md:h-20 md:w-20"
/>
</a>
</Link>
<nav className="ml-2">
<ul className="flex items-stretch h-16 md:h-20">
{/* TODO: Add document page and remove noIndex from docs layout */}
{/*
<li className="relative flex items-center justify-center">
<Link href="/docs">
<a
className={classNames('custom-anchor header-item nav-item', {
active: activeItem === 'docs',
})}
role="link"
tabIndex={0}
onMouseUp={handleMouseUp}
>
<span>Docs</span>
</a>
</Link>
</li>
*/}
<li className="relative flex items-center justify-center">
<Link href="/blog">
<a
className={classNames('custom-anchor header-item nav-item', {
active: activeItem === 'blog',
})}
role="link"
tabIndex={0}
onMouseUp={handleMouseUp}
>
<span>Blog</span>
</a>
</Link>
</li>
<li className="relative flex items-center justify-center">
<a
href="https://github.com/marp-team/marp"
target="_blank"
rel="noopener noreferrer"
className="custom-anchor header-item nav-item"
onMouseUp={handleMouseUp}
>
<span>GitHub</span>
</a>
</li>
</ul>
</nav>
<style jsx>{`
.header-item {
@apply no-underline text-current outline-none;
}
.header-item > img {
@apply transition-transform duration-200;
}
.header-item:hover:active > img {
@apply transform scale-125 shadow-none;
-webkit-tap-highlight-color: transparent;
transition-duration: 0ms;
}
@media not all and (hover: none) {
.header-item:hover:active > img {
@apply scale-110;
}
}
.nav-item {
@apply font-rounded font-medium mx-2 uppercase text-lg leading-none outline-none;
}
.nav-item::before {
@apply absolute inset-0;
content: '';
}
.header-item:focus-visible,
.nav-item:focus-visible::before {
@apply bg-gray-200;
}
@screen md {
.nav-item {
@apply mx-3 tracking-wider;
}
}
.nav-item > span {
@apply relative z-10;
}
.nav-item > span::after {
@apply absolute block inset-x-0 h-1 mt-1 transition-all duration-300;
content: '';
top: 100%;
}
.nav-item:hover > span::after,
.nav-item:focus-within > span::after {
box-shadow: inset 0 -0.25rem theme('colors.gray.400');
}
.nav-item.active > span::after {
transition-duration: 0ms;
box-shadow: inset 0 -0.25rem theme('colors.marp.brand');
}
.nav-item:hover:active > span::after {
transition-duration: 0ms;
box-shadow: inset 0 -0.25rem theme('colors.marp.dark');
}
`}</style>
</header>
</>
)

View File

@ -0,0 +1,91 @@
import Head from 'next/head'
import { useRouter } from 'next/router'
import { Footer } from 'components/Footer'
import { Header, ItemSlug } from 'components/Header'
import { generateTitle } from 'utils/title'
import { absoluteUrl } from 'utils/url'
export type LayoutProps = {
activeItem?: ItemSlug
canonical?: string
description?: string
image?: string
noIndex?: boolean
title?: string | string[]
type?: string
}
const defaultDescription =
'Marp, Markdown Presentation Ecosystem, provides the great experience to create beautiful slide deck. You only have to focus writing your story in Markdown document.'
export const Layout: React.FC<LayoutProps> = ({
activeItem,
canonical: _canonical,
children,
description = defaultDescription,
image: _image,
noIndex,
title: _title,
type = 'article',
}) => {
const router = useRouter()
const canonical = absoluteUrl(_canonical || router.asPath).href
const image = _image || '/assets/og-image.png'
const title = typeof _title === 'string' ? _title : generateTitle(_title)
return (
<>
<Head>
<title key="title">{title}</title>
{description && (
<>
<meta name="description" key="description" content={description} />
<meta
property="og:description"
key="og:description"
content={description}
/>
</>
)}
{canonical && (
<>
<link rel="canonical" key="canonical" href={canonical} />
<meta property="og:url" key="og:url" content={canonical} />
</>
)}
<meta property="og:title" key="og:title" content={title} />
<meta property="og:type" key="og:type" content={type} />
<meta
property="og:image"
key="og:image"
content={absoluteUrl(image).href}
/>
<meta
property="twitter:card"
key="twitter:card"
content={
type === 'website' || _image ? 'summary_large_image' : 'summary'
}
/>
{noIndex && <meta name="robots" content="noindex,nofollow" />}
</Head>
<Header activeItem={activeItem} />
<main className="relative mt-16 md:mt-20">
{children}
<style jsx>{`
main {
min-height: calc(100vh - 8.5rem);
}
@screen md {
main {
min-height: calc(100vh - 9.5rem);
}
}
`}</style>
</main>
<Footer />
</>
)
}

View File

@ -0,0 +1,51 @@
import { Marp as MarpCore } from '@marp-team/marp-core'
import { browser } from '@marp-team/marp-core/browser'
import classNames from 'classnames'
import { useEffect, useRef } from 'react'
export type RenderedMarp = ReturnType<typeof generateRenderedMarp>
export type MarpProps = {
className?: string
rendered: RenderedMarp
page?: number
}
export const generateRenderedMarp = (markdown: string) => {
const marp = new MarpCore({
container: false,
script: false,
printable: false,
})
return { markdown, ...marp.render(markdown, { htmlAsArray: true }) }
}
export const Marp = ({
className,
rendered: { css, html },
page = 1,
}: MarpProps) => {
const element = useRef<HTMLDivElement>(null)
useEffect(() => {
if (!element.current) return
if (!element.current.shadowRoot)
element.current.attachShadow({ mode: 'open' })
// Render Marp slide to shadow root (tailwind default styles will break Marp slide CSS)
const root = element.current.shadowRoot as ShadowRoot
root.innerHTML =
html[page - 1] +
`<style>${css}</style><style>:host{all:initial;}:host>[data-marpit-svg]{vertical-align:top;}</style>`
return browser(root)
}, [css, html, page])
return (
<div className={classNames('border shadow-lg', className)}>
<span ref={element} />
</div>
)
}

View File

@ -0,0 +1,43 @@
import { useCallback } from 'react'
export const ScrollToTop = () => {
const handleClick = useCallback<React.MouseEventHandler<HTMLElement>>((e) => {
window.scrollTo({ top: 0 })
e.currentTarget.blur()
}, [])
return (
<div className="scroll-to-top">
<button onClick={handleClick} title="Scroll to top">
<span className="sr-only">Scroll to top</span>
</button>
<style jsx>{`
.scroll-to-top {
@apply fixed right-0 bottom-0 z-50 pointer-events-none;
filter: drop-shadow(0 0px 7px rgba(0, 0, 0, 0.3))
drop-shadow(0 0px 4px rgba(0, 0, 0, 0.15));
}
button {
@apply appearance-none text-white w-20 h-20 bg-marp-light align-top pointer-events-auto;
background-image: url('https://icongr.am/octicons/arrow-up.svg?color=ffffff');
background-position: 80% 80%;
background-repeat: no-repeat;
background-size: 35% 35%;
clip-path: polygon(100% 0, 100% 100%, 0 100%);
}
button:hover {
@apply bg-marp-brand;
}
button:focus {
@apply outline-none;
}
button:focus,
button:hover:active {
@apply bg-marp-dark;
}
`}</style>
</div>
)
}

View File

@ -0,0 +1,17 @@
export const Title: React.FC = ({ children }) => (
<section className="border-b bg-marp-brand text-white py-3">
<h1 className="text-3xl font-bold text-center font-rounded uppercase">
{children}
<style jsx>{`
& :global(a),
& :global(a:hover),
& :global(a:hover:active) {
@apply no-underline text-current;
}
& :global(a:focus-visible) {
@apply underline outline-none;
}
`}</style>
</h1>
</section>
)

View File

@ -0,0 +1,133 @@
export const Typography: React.FC = ({ children }) => (
<div className="typography">
{children}
<style jsx>{`
.typography {
@apply break-words leading-relaxed text-base;
}
.typography :global(p) {
@apply my-4;
}
.typography :global(h1) {
@apply relative font-bold text-3xl mt-8 mb-5;
}
.typography :global(h2) {
@apply relative font-bold text-2xl mt-8 mb-5;
}
.typography :global(h3) {
@apply relative font-bold text-xl mt-8 mb-4;
}
.typography :global(h4) {
@apply relative font-bold text-lg mt-6 mb-4;
}
.typography :global(h5) {
@apply relative font-bold text-base mt-6 mb-4;
}
.typography :global(h6) {
@apply relative font-bold text-sm text-gray-600 mt-6 mb-4;
}
.typography :global(.anchor-link) {
@apply absolute inset-0 w-5 -ml-5 overflow-hidden whitespace-no-wrap my-auto bg-no-repeat bg-left hidden;
background-size: 1rem 1rem;
background-image: url('https://icongr.am/octicons/link.svg?color=718096');
}
.typography :global(h1:hover > .anchor-link),
.typography :global(h2:hover > .anchor-link),
.typography :global(h3:hover > .anchor-link),
.typography :global(h4:hover > .anchor-link),
.typography :global(h5:hover > .anchor-link),
.typography :global(h6:hover > .anchor-link) {
@apply block;
}
.typography :global(hr) {
@apply my-8;
}
.typography :global(blockquote) {
@apply text-gray-600 border-l-4 border-marp-light pl-5 my-6;
}
.typography :global(blockquote blockquote) {
border-left-width: 3px;
}
.typography :global(blockquote blockquote blockquote) {
@apply border-l-2;
}
.typography :global(ul) {
@apply list-disc ml-8 mr-3 my-6;
}
.typography :global(ul ul) {
list-style-type: circle;
}
.typography :global(ul ul ul) {
list-style-type: square;
}
.typography :global(ol:not(.code-block)) {
@apply list-decimal ml-8 mr-3 my-6;
}
.typography :global(ul ul),
.typography :global(ul ol:not(._)),
.typography :global(ol:not(._) ul),
.typography :global(ol:not(._) ol:not(._)) {
@apply my-0 mr-0;
}
.typography :global(li:not(.code-block)) {
@apply my-1;
}
.typography :global(code:not(.code-block)) {
@apply bg-gray-200 border rounded border-gray-400;
font-size: 0.9em;
padding: 0.15em 0.35em;
}
.typography :global(pre) {
@apply my-6;
}
.typography :global(img) {
@apply inline;
}
.typography :global(figure) {
@apply my-6;
}
.typography :global(figure img) {
@apply block mx-auto max-w-screen-md w-full;
}
.typography :global(figcaption) {
@apply text-gray-600 text-sm text-center mx-auto w-11/12 my-4;
}
.typography :global(table) {
@apply max-w-full mx-auto my-8;
}
.typography :global(td),
.typography :global(th) {
@apply p-2 border-b border-gray-500 text-sm;
}
.typography :global(thead tr:last-child td),
.typography :global(thead tr:last-child th) {
@apply border-b-2;
}
@screen sm {
.typography :global(td),
.typography :global(th) {
@apply py-2 px-4;
}
}
@screen md {
.typography :global(td),
.typography :global(th) {
@apply text-base;
}
}
.typography > :global(*:first-child),
.typography > :global(*:first-child *:first-child) {
@apply mt-0;
}
.typography > :global(*:last-child),
.typography > :global(*:last-child *:last-child) {
@apply mb-0;
}
`}</style>
</div>
)

View File

@ -0,0 +1,85 @@
import Link from 'next/link'
import { formatDate, formatDateShort } from 'utils/date'
export type BlogHeaderProps = {
author?: string
date?: Date
github?: string
slug: string
title: string
}
export const BlogHeader = ({
author,
date,
github,
slug,
title,
}: BlogHeaderProps) => (
<div className="text-center text-gray-600">
<Link href={`/blog/${slug}`}>
<a>
<h1 className="text-gradient text-3xl font-bold md:text-4xl">
{title}
</h1>
</a>
</Link>
{date && (
<p className="mt-4">
<time dateTime={formatDateShort(date)}>{formatDate(date)}</time>
</p>
)}
<p className="author">
{(author || github) && (
<>
{github && (
<img
src={`https://github.com/${github}.png`}
alt={author || github}
className="h-16 w-16 rounded-full shadow-md bg-white mr-4"
/>
)}
<span className="leading-relaxed">
by{' '}
{author && (
<>
{author}
{github && <br />}
</>
)}
{github && (
<a
href={`https://github.com/${github}`}
target="_blank"
rel="noopener noreferrer"
>
@{github}
</a>
)}
</span>
</>
)}
<style jsx>{`
.author {
@apply mt-5 flex text-left items-center -mx-6;
}
.author::before,
.author::after {
@apply block flex-1 h-px mx-6 bg-gray-400;
content: '';
}
.author:empty {
@apply h-px mx-0;
}
.author:empty::before,
.author:empty::after {
@apply mx-0;
}
`}</style>
</p>
</div>
)

View File

@ -0,0 +1,204 @@
import classNames from 'classnames'
import { useState } from 'react'
import { useMedia } from 'use-media'
import { Navigation } from './Navigation'
import { useDrawer } from './useDrawer'
import { Layout } from 'components/Layout'
export type LayoutProps = {
breadcrumbs?: React.ReactNode[]
}
const PageLayout: React.FC<LayoutProps> = ({ breadcrumbs = [], children }) => {
const [drawer, setDrawer] = useState<HTMLElement | null>(null)
const drawerEnabled = !useMedia({ minWidth: '768px' })
const { active, handleClose, handleOpen, open } = useDrawer({
drawer,
enabled: drawerEnabled,
})
return (
<Layout
activeItem="docs"
title={['Docs']}
noIndex // TODO: Remove noIndex
>
<div className="docs-container">
{drawerEnabled && (
<div
className={classNames('docs-backdrop', { active, open })}
onClick={handleClose}
aria-hidden
></div>
)}
<nav
className={classNames('docs-nav', {
active,
open,
enabled: drawerEnabled,
})}
ref={setDrawer}
tabIndex={drawerEnabled && !open ? -1 : undefined}
aria-hidden={drawerEnabled && !open}
>
{drawerEnabled && (
<button
className="docs-nav-button docs-nav-button-close"
onClick={handleClose}
>
<img
src="https://icongr.am/octicons/x.svg?color=4a5568"
alt="Close navigation"
className="w-8 h-8"
/>
</button>
)}
<Navigation />
</nav>
<article className="docs-article">
<nav className="docs-breadcrumb">
{drawerEnabled && (
<button className="docs-nav-button" onClick={handleOpen}>
<img
src="https://icongr.am/octicons/three-bars.svg?color=4a5568"
alt="Open navigation"
className="p-1 w-8 h-8"
/>
</button>
)}
{!!breadcrumbs.length && (
<ol>
{breadcrumbs.map((el, i) => (
<li key={i}>{el}</li>
))}
</ol>
)}
</nav>
<section className="container mx-auto md:px-4">{children}</section>
</article>
<style jsx>{`
.docs-container {
@apply relative flex text-sm;
min-height: inherit;
}
.docs-backdrop {
@apply fixed inset-0 opacity-0 transition-opacity pointer-events-none cursor-pointer;
-webkit-tap-highlight-color: transparent;
backdrop-filter: blur(2px);
background: rgba(255, 255, 255, 0.5);
z-index: 60;
}
.docs-backdrop.active {
@apply duration-300;
}
.docs-backdrop.open {
@apply pointer-events-auto opacity-100;
}
.docs-nav {
@apply px-8 my-8 w-64 border-r;
}
.docs-nav.enabled {
@apply fixed inset-0 bg-background my-0 py-8 overflow-auto transform -translate-x-full;
transition-property: box-shadow, transform;
z-index: 60;
}
.docs-nav.enabled.active {
@apply duration-300;
}
.docs-nav.enabled.open {
@apply transform-none shadow-2xl;
}
.docs-article {
@apply flex-1 relative p-8 mt-12;
}
.docs-breadcrumb {
@apply fixed inset-x-0 h-12 flex items-center px-4 bg-white text-gray-600 shadow-sm z-50;
top: 4rem;
}
.docs-breadcrumb::after {
@apply absolute inset-0 w-4 h-full pointer-events-none;
left: 3rem;
background: linear-gradient(
to right,
theme('colors.white') 50%,
rgba(255, 255, 255, 0)
);
content: '';
}
.docs-breadcrumb ol {
@apply relative flex items-center justify-end overflow-x-hidden h-full;
}
.docs-breadcrumb li {
@apply block whitespace-no-wrap;
}
.docs-breadcrumb li::before {
@apply pl-6 bg-no-repeat;
background-image: url('https://icongr.am/octicons/triangle-right.svg?color=718096');
background-position: 0.25rem center;
background-size: 1rem 1rem;
content: '';
}
.docs-breadcrumb li:first-child::before {
@apply pl-4;
background-image: none;
}
.docs-nav-button {
@apply flex-shrink-0 appearance-none rounded h-8 w-8 outline-none;
}
.docs-nav-button:focus-visible {
@apply bg-gray-200;
}
.docs-nav-button > img {
@apply transition-transform duration-200;
}
.docs-nav-button > img:hover:active {
@apply transform scale-125;
transition-duration: 0ms;
}
.docs-nav-button-close {
@apply mb-6 -ml-1;
}
@screen md {
.docs-article {
@apply mt-0;
}
.docs-breadcrumb {
@apply static bg-transparent shadow-none px-0 h-auto mb-6;
}
.docs-breadcrumb::after,
.docs-breadcrumb li:first-child::before {
@apply hidden;
}
}
@screen xl {
.docs-container {
@apply text-base;
}
.docs-nav {
@apply w-3/12;
}
}
`}</style>
</div>
</Layout>
)
}
export { PageLayout as Layout }

View File

@ -0,0 +1,27 @@
export const Navigation = () => (
<div className="xl:w-5/6 xl:mx-auto">
<ul>
<li>
<h3 className="font-rounded font-bold text-gray-600 uppercase text-lg xl:text-xl">
Concept
</h3>
<ul>
<li className="mt-3">What&apos;s Marp?</li>
</ul>
<ul>
<li className="mt-3">Ecosystem</li>
</ul>
</li>
</ul>
<ul className="mt-8">
<li>
<h3 className="font-rounded font-bold text-gray-600 uppercase text-lg xl:text-xl">
Markdown
</h3>
<ul>
<li className="mt-3">How to write slides</li>
</ul>
</li>
</ul>
</div>
)

View File

@ -0,0 +1,55 @@
import { disableBodyScroll, enableBodyScroll } from 'body-scroll-lock'
import { useRouter } from 'next/router'
import { useCallback, useEffect, useRef, useState } from 'react'
export type UseDrawerOptions = {
drawer?: HTMLElement | null
duration?: number
enabled?: boolean
}
export const useDrawer = ({
drawer,
duration = 300,
enabled = true,
}: UseDrawerOptions = {}) => {
const router = useRouter()
const [open, setOpen] = useState(false)
const [active, setActive] = useState(false)
const activeTimer = useRef<number>()
const handleOpen = useCallback(() => {
setOpen(true)
setActive(true)
}, [])
const handleClose = useCallback(() => {
setOpen(false)
setActive(true)
}, [])
const toggle = useCallback(() => {
open ? handleClose() : handleOpen()
}, [open, handleOpen, handleClose])
useEffect(() => {
router.events.on('routeChangeComplete', handleClose)
return () => router.events.off('routeChangeComplete', handleClose)
}, [handleClose, router.events])
useEffect(() => {
if (drawer && enabled && open) {
disableBodyScroll(drawer)
return () => enableBodyScroll(drawer)
}
}, [drawer, enabled, open])
useEffect(() => {
if (active && enabled) {
if (activeTimer.current !== undefined)
window.clearTimeout(activeTimer.current)
activeTimer.current = window.setTimeout(() => setActive(false), duration)
}
}, [active, duration, enabled])
return { active, handleClose, handleOpen, toggle, open: enabled && open }
}

View File

@ -0,0 +1,74 @@
import classNames from 'classnames'
import { useState } from 'react'
import { Button } from 'components/Button'
import { CodeBlock } from 'components/CodeBlock'
import { Marp, RenderedMarp } from 'components/Marp'
export type DescriptionProps = {
example: RenderedMarp
}
export const Description = ({ example }: DescriptionProps) => {
const [showExample, setShowExample] = useState(false)
return (
<section className="container mx-auto py-16">
<h2 className="w-5/6 mx-auto text-gradient text-center text-3xl font-bold md:text-4xl">
The great experience to create slide deck with Markdown
</h2>
<p className="w-5/6 mx-auto mt-8 md:text-lg lg:w-2/3">
Marp, Markdown Presentation Ecosystem, provides the great experience to
create beautiful slide deck. You only have to focus writing your story
in Markdown document.
</p>
<figure className="text-center m-8 mb-0">
<Marp
rendered={example}
page={1}
className="max-w-sm w-full inline-block"
/>
<Marp
rendered={example}
page={2}
className="max-w-sm w-full inline-block mt-5 lg:ml-5 lg:mt-0"
/>
<figcaption className="mt-5 text-sm text-gray-500">
We&apos;re rendering slides generated in{' '}
<a href="https://github.com/marp-team/marp-core">Marp Core</a>
</figcaption>
</figure>
<p className="text-center mt-8 mx-auto w-5/6">
<Button
className="text-sm"
onClick={() => setShowExample((v) => !v)}
aria-expanded={showExample}
>
{showExample ? 'Hide' : 'Show'} Markdown example...
<img
className={classNames(
'inline w-4 h-4 ml-1 transform transition-transform duration-300 md:w-6 md:h-6 md:-my-1',
showExample && '-rotate-180'
)}
style={showExample ? {} : { verticalAlign: 'sub' }}
src="https://icongr.am/octicons/chevron-down.svg?color=4a5568"
alt=""
/>
</Button>
</p>
<div
aria-hidden={!showExample}
className="overflow-hidden transition-all duration-300"
style={{ maxHeight: showExample ? '1000px' : '0' }}
>
<CodeBlock
language="markdown"
lineNumber
className="mx-auto mt-5 w-5/6 xl:w-2/3"
copyButton={showExample}
>
{example.markdown}
</CodeBlock>
</div>
</section>
)
}

View File

@ -0,0 +1,300 @@
type CardProps = {
name: string
icon: string
index: number
}
const Card: React.FC<CardProps> = ({ children, name, icon, index }) => (
<section className="card">
<div>
<img
className="mx-auto w-12 h-12 m-2 lg:w-16 lg:h-16"
src={icon}
alt={name}
/>
<h3 className="text-gradient text-2xl text-center font-semibold my-4">
{name}
</h3>
<p className="text-sm lg:text-base">{children}</p>
</div>
<style jsx>{`
.card {
@apply flex justify-center items-center bg-white shadow-lg rounded-lg mx-4 my-8 p-6 mb-0 relative z-10;
grid-column: 1;
}
@screen md {
.card {
grid-row: ${index + 1} / span 2;
}
.card:first-child {
@apply mt-0;
}
.card:nth-of-type(even) {
grid-column: 2;
}
}
`}</style>
</section>
)
const cards = [
({ index }) => (
<Card
index={index}
name="Based on CommonMark"
icon="https://icongr.am/octicons/markdown.svg?color=4a5568"
>
If you know how to write document with Markdown, you already know how to
write Marp slide deck too. Our format is based on{' '}
<a
href="https://commonmark.org/"
target="_blank"
rel="noopener noreferrer"
>
CommonMark
</a>
, the consistent spec of Markdown. The only important difference is{' '}
<a
href="https://marpit.marp.app/markdown"
rel="noopener noreferrer"
target="_blank"
>
a ruler <code>---</code> for splitting pages.
</a>
</Card>
),
({ index }) => (
<Card
index={index}
name="Directives and extended syntax"
icon="https://icongr.am/octicons/code-square.svg?color=4a5568"
>
Nevertheless, you may think the simple text content is lacking to
emphasize your voice. We are supporting to create beautiful slide through{' '}
<a
href="https://marpit.marp.app/directives"
rel="noopener noreferrer"
target="_blank"
>
directives
</a>{' '}
and extended syntax (
<a
href="https://marpit.marp.app/image-syntax"
rel="noopener noreferrer"
target="_blank"
>
Image syntax
</a>
,{' '}
<a
href="https://github.com/marp-team/marp-core#math-typesetting"
rel="noopener noreferrer"
target="_blank"
>
math typesetting
</a>
,{' '}
<a
href="https://github.com/marp-team/marp-core#auto-scaling-features"
rel="noopener noreferrer"
target="_blank"
>
auto-scaling
</a>
, etc...).
</Card>
),
({ index }) => (
<Card
index={index}
name="Built-in themes and CSS theming"
icon="https://icongr.am/octicons/paintbrush.svg?color=4a5568"
>
<a
href="https://github.com/marp-team/marp-core/"
rel="noopener noreferrer"
target="_blank"
>
Our core engine
</a>{' '}
has{' '}
<a
href="https://github.com/marp-team/marp-core/tree/master/themes"
rel="noopener noreferrer"
target="_blank"
>
3 built-in themes called <code>default</code>, <code>gaia</code>, and{' '}
<code>uncover</code>
</a>
, to tell your story beautifully. If you are feeling unsatisfied to
design, Marp can{' '}
<a
href="https://marpit.marp.app/theme-css?id=tweak-style-through-markdown"
rel="noopener noreferrer"
target="_blank"
>
tweak style through Markdown
</a>
, or{' '}
<a
href="https://marpit.marp.app/theme-css"
rel="noopener noreferrer"
target="_blank"
>
create your own theme with plain CSS
</a>
.
</Card>
),
({ index }) => (
<Card
index={index}
name="Export to HTML, PDF, and PowerPoint"
icon="https://icongr.am/octicons/file.svg?color=4a5568"
>
Have you finished writing? Let&apos;s share the deck with a favorite way!
We can convert Markdown into presentation-ready HTML, what is more, PDF
and PowerPoint document directly! (Powered by{' '}
<a
href="https://www.google.com/chrome/"
rel="noopener noreferrer"
target="_blank"
>
Google Chrome
</a>{' '}
/{' '}
<a
href="https://www.chromium.org/Home"
rel="noopener noreferrer"
target="_blank"
>
Chromium
</a>
)
</Card>
),
({ index }) => (
<Card
index={index}
name="Marp family: The official toolset"
icon="https://icongr.am/octicons/package.svg?color=4a5568"
>
Marp family has the rich toolset to assist your work.{' '}
<a
href="https://marketplace.visualstudio.com/items?itemName=marp-team.marp-vscode"
rel="noopener noreferrer"
target="_blank"
>
<b>Marp for VS Code</b>
</a>{' '}
extension can preview editting Markdown and custom theme immediately.{' '}
<a
href="https://github.com/marp-team/marp-cli/"
rel="noopener noreferrer"
target="_blank"
>
<b>Marp CLI</b>
</a>{' '}
allows to convert Markdown through CLI interface.{' '}
<a
href="https://github.com/marp-team/marp/"
rel="noopener noreferrer"
target="_blank"
>
...and more!
</a>
</Card>
),
({ index }) => (
<Card
index={index}
name="Pluggable architecture"
icon="https://icongr.am/octicons/plug.svg?color=4a5568"
>
As a matter of fact,{' '}
<em>Marp is essentially just a converter for Markdown.</em> Marp ecosystem
is built on{' '}
<a
href="https://marpit.marp.app"
rel="noopener noreferrer"
target="_blank"
>
<b>Marpit framework</b>
</a>
, the skinny framework for creating HTML + CSS slide deck. It has a
pluggable architecture and developer can{' '}
<a
href="https://marpit.marp.app/usage?id=extend-marpit-by-plugins"
rel="noopener noreferrer"
target="_blank"
>
extend features via plugin
</a>
.
</Card>
),
({ index }) => (
<Card
index={index}
name="Fully open-source"
icon="https://icongr.am/octicons/heart-fill.svg?color=4a5568"
>
We are loving open source! All tools and related libraries by{' '}
<a
href="https://github.com/marp-team"
rel="noopener noreferrer"
target="_blank"
>
Marp team
</a>{' '}
are licensed by MIT License.
</Card>
),
]
export const Features = () => (
<div className="features">
<div className="container features-grid">
{cards.map((Card, i) => (
<Card index={i} key={i} />
))}
</div>
<style jsx>{`
.features {
@apply relative py-5;
}
.features::before {
@apply absolute block inset-0;
background-image: url('/assets/noise.png'),
linear-gradient(
-8deg,
rgba(120, 197, 233, 0),
rgba(120, 197, 233, 0) 50%,
rgba(120, 197, 233, 0.5)
);
clip-path: polygon(0 15vw, 100% 0, 100% 100%, 0 100%);
content: '';
}
.features-grid {
@apply grid mx-auto px-4;
grid-template-columns: 1fr;
grid-template-rows: repeat(${cards.length + 1}, auto);
}
@screen md {
.features-grid {
grid-template-columns: 1fr 1fr;
}
}
`}</style>
</div>
)

View File

@ -0,0 +1,327 @@
import classNames from 'classnames'
import { Button } from 'components/Button'
type CardProps = {
badge?: string
className?: string
description: string
href: string
name: string
screenShot?: string
summary: string
}
const Card: React.FC<CardProps> = ({
badge,
children,
className,
description,
href,
name,
screenShot: screenshot,
summary,
}) => (
<section className={classNames('card', className, { screenshot })}>
<a
href={href}
target="_blank"
rel="noopener noreferrer"
className="custom-anchor card-link"
>
<h4 className="inline-block font-bold text-gradient pr-3 pb-1 text-xl sm:text-2xl md:text-3xl">
{name}
</h4>
{badge && (
<img
src={badge}
alt=""
className="inline rounded-sm align-text-top sm:align-baseline"
loading="lazy"
/>
)}
<p className="text-gray-600 text-sm pt-1 leading-tight">{summary}</p>
</a>
{screenshot && (
<figure>
<img src={screenshot} alt={name} loading="lazy" />
</figure>
)}
<p className="mx-5 lg:my-3">{description}</p>
<p className="text-sm mx-5 my-4">{children}</p>
<style jsx>{`
.card {
@apply bg-white text-foreground relative grid my-8 p-2 rounded-lg shadow-xl overflow-hidden;
grid-template-columns: 1fr;
}
.card::before {
@apply absolute right-0 w-40 h-40 opacity-25 transform bg-center bg-no-repeat bg-contain;
content: '';
top: -2rem;
transform: rotate(-15deg);
}
.card.vscode::before {
background-image: url('https://icongr.am/simple/visualstudiocode.svg?color=67b8e3');
}
.card.cli::before {
background-image: url('https://icongr.am/octicons/terminal.svg?color=67b8e3');
}
.card.core::before {
background-image: url('/assets/marp-logo.svg');
}
.card.marpit::before {
background-image: url('/assets/marpit.svg');
}
.card > * {
@apply relative col-start-1 col-end-1;
}
.card > figure {
@apply flex justify-center items-center mx-auto p-4;
}
.card > figure > img {
@apply max-w-full;
width: 28rem;
}
.card-link {
@apply relative block p-5 transition-all duration-150 rounded z-10;
}
.card-link:hover,
.card-link:focus {
@apply shadow;
background-color: rgba(255, 255, 255, 0.5);
}
.card-link:hover:active,
.card-link:focus {
@apply shadow-outline outline-none;
transition-duration: 0ms;
}
@screen lg {
.card.screenshot {
grid-template-columns: 3fr 2fr;
}
.card > figure {
@apply col-start-2 col-end-2 w-full h-full object-contain px-6 py-0;
grid-row: 1 / span 9999;
}
.card::before {
@apply w-64 h-64;
top: -4rem;
}
}
@screen xl {
.card {
@apply w-5/6 mx-auto;
}
}
`}</style>
</section>
)
export const GetStarted = () => (
<>
<a id="get-started" className="named-anchor">
Get started
</a>
<div className="get-started clearfix">
<section className="container mx-auto py-10 px-8 lg:px-16">
<h3 className="font-bold text-center text-2xl sm:text-3xl">
<mark>Tools and integrations</mark>
</h3>
<Card
description="Enhance VS Code's Markdown preview pane to support writing your beautiful presentation. You can see the slide deck output as soon as editting Markdown."
name="Marp for VS Code"
summary="Create slide deck written in Marp Markdown, in VS Code"
badge="https://img.shields.io/visual-studio-marketplace/v/marp-team.marp-vscode.svg?style=flat-square&amp;label=&amp;colorB=0288d1"
href="https://marketplace.visualstudio.com/items?itemName=marp-team.marp-vscode"
screenShot="/assets/marp-for-vs-code.png"
className="vscode"
>
<Button
color="primary"
className="mr-2 mb-2"
href="https://marketplace.visualstudio.com/items?itemName=marp-team.marp-vscode"
target="_blank"
rel="noopener noreferrer"
>
VS Marketplace
</Button>
<Button
outline
className="mr-2 mb-2"
href="https://github.com/marp-team/marp-vscode"
target="_blank"
rel="noopener noreferrer"
>
GitHub
</Button>
</Card>
<Card
description="CLI is the swiss army knife for Marp ecosystem. Convert your Markdown into various formats, watch changes, launch server for on-demand conversion, and customize engine."
name="Marp CLI"
summary="A CLI interface for Marp and Marpit based converters"
badge="https://img.shields.io/npm/v/@marp-team/marp-cli.svg?style=flat-square&amp;label=&amp;colorB=0288d1"
href="https://github.com/marp-team/marp-cli"
screenShot="/assets/marp-cli.png"
className="cli"
>
<Button
color="primary"
className="mr-2 mb-2"
href="https://github.com/marp-team/marp-cli/releases"
target="_blank"
rel="noopener noreferrer"
>
Releases
</Button>
<Button
outline
color="primary"
className="mr-2 mb-2"
href="https://www.npmjs.com/package/@marp-team/marp-cli"
target="_blank"
rel="noopener noreferrer"
>
npm
</Button>
<Button
outline
className="mr-2 mb-2"
href="https://github.com/marp-team/marp-cli"
target="_blank"
rel="noopener noreferrer"
>
GitHub
</Button>
</Card>
<h3 className="font-bold text-center text-2xl sm:text-3xl">
<mark>For developers</mark>
</h3>
<Card
description="All official Marp tools provided by us are using this core as the engine. It is based on Marpit framework, and includes some extended features to help creating beautiful slide deck."
name="Marp Core"
summary="The core of Marp converter"
badge="https://img.shields.io/npm/v/@marp-team/marp-core.svg?style=flat-square&amp;label=&amp;colorB=0288d1"
href="https://github.com/marp-team/marp-core"
className="core"
>
<Button
outline
color="primary"
className="mr-2 mb-2"
href="https://www.npmjs.com/package/@marp-team/marp-core"
target="_blank"
rel="noopener noreferrer"
>
npm
</Button>
<Button
outline
className="mr-2 mb-2"
href="https://github.com/marp-team/marp-core"
target="_blank"
rel="noopener noreferrer"
>
GitHub
</Button>
</Card>
<Card
description="Marpit, independented from Marp, is the skinny framework to transform Markdown + CSS theme to the deck composed of HTML + CSS. It has designed to output only minimum assets."
name="Marpit framework"
summary="The skinny framework for creating slide deck from Markdown"
badge="https://img.shields.io/npm/v/@marp-team/marpit.svg?style=flat-square&amp;label=&amp;colorB=0288d1"
href="https://marpit.marp.app/"
className="marpit"
>
<Button
color="primary"
className="mr-2 mb-2"
href="https://marpit.marp.app/"
target="_blank"
rel="noopener noreferrer"
>
Documentation
</Button>
<Button
outline
color="primary"
className="mr-2 mb-2"
href="https://www.npmjs.com/package/@marp-team/marpit"
target="_blank"
rel="noopener noreferrer"
>
npm
</Button>
<Button
outline
className="mr-2 mb-2"
href="https://github.com/marp-team/marpit"
target="_blank"
rel="noopener noreferrer"
>
GitHub
</Button>
</Card>
<p className="text-center mt-4">
...and find out all tools, integrations, examples at our GitHub
entrance repository!
</p>
<p className="text-center text-sm text-foreground mt-4">
<Button
href="https://github.com/marp-team/marp/"
rel="noopener"
target="_blank"
>
Go to the entrance repository of Marp...
</Button>
</p>
</section>
</div>
<style jsx>{`
.get-started {
@apply relative bg-marp-brand text-white;
background-image: url('/assets/noise.png'),
linear-gradient(
-2deg,
theme('colors.marp.darken'),
theme('colors.marp.brand') 500px
);
}
.get-started::before,
.get-started::after {
@apply block absolute inset-x-0;
background-image: url('/assets/noise.png'),
linear-gradient(to bottom, rgba(255, 255, 255, 0.4), transparent 95%);
bottom: calc(100% - 5px);
content: '';
transform: translateZ(0);
z-index: -1;
}
.get-started::before {
@apply bg-marp-light;
clip-path: polygon(0 0, 100% 90%, 100% 100%, 0 100%);
height: calc(120px + 5vw);
}
.get-started::after {
@apply bg-marp-brand;
clip-path: polygon(0 0, 100% 95%, 100% 100%, 0 100%);
height: calc(60px + 5vw);
}
`}</style>
</>
)

View File

@ -0,0 +1,53 @@
import Head from 'next/head'
import { Button } from 'components/Button'
const marp = '/assets/marp.svg' as const
const heroBg = '/assets/hero-background.jpg' as const
export const Hero = () => (
<>
<Head>
<link rel="preload" href={heroBg} as="image" />
<link rel="preload" href={marp} as="image" />
</Head>
<section className="py-16 px-4 border-b md:py-24 md:tracking-wider">
<h1 className="text-center font-rounded font-bold sm:text-xl md:text-2xl">
<img
src={marp}
alt="Marp: Markdown Presentation Ecosystem"
className="mx-auto w-4/5 h-auto max-w-xl mb-5 p-3"
width={1045}
height={320}
/>
Markdown Presentation Ecosystem
</h1>
<p className="text-center mt-10">
<Button
href="#get-started"
color="primary"
className="text-xl md:text-2xl"
>
Get started!
</Button>
</p>
<p className="text-center mt-5">
<Button
href="https://github.com/marp-team/marp"
target="_blank"
rel="noopener noreferrer"
className="text-sm md:text-base"
color="primary"
outline
>
Find out Marp tools at GitHub...
</Button>
</p>
<style jsx>{`
section {
background: #fcfcfc url('${heroBg}') no-repeat right center;
background-size: cover;
}
`}</style>
</section>
</>
)

82
website/css/index.css Normal file
View File

@ -0,0 +1,82 @@
@tailwind base;
@tailwind components;
@tailwind utilities;
html:not(.translating) {
scroll-behavior: smooth;
}
body {
@apply relative text-foreground bg-background min-h-full;
}
body::before {
@apply fixed block inset-0 bg-background;
background-image: url('/assets/noise.png'),
linear-gradient(
to bottom,
theme('colors.gray.100'),
theme('colors.background') 50%
);
content: '';
z-index: -1;
}
a:not(.custom-anchor) {
@apply text-marp-brand;
}
a:not(.custom-anchor):hover {
@apply underline text-marp-dark transition-colors duration-300;
}
a:not(.custom-anchor):hover:active {
@apply text-marp-darkest;
transition-duration: 0ms;
}
a.named-anchor {
@apply invisible block h-0 relative;
top: -4rem;
}
@screen md {
a.named-anchor {
top: -5rem;
}
}
mark {
background: none;
color: inherit;
box-shadow: inset 0 -0.2em theme('colors.marp.light');
}
/* NProgress */
#nprogress .bar {
@apply bg-marp-brand;
}
#nprogress .peg {
@apply shadow-none;
}
/* Helper classes */
.text-gradient {
@apply text-marp-brand leading-tight;
}
@supports (background-clip: text) {
.text-gradient {
@apply text-transparent bg-marp-brand bg-clip-text;
background-image: linear-gradient(
-1deg,
theme('colors.marp.light'),
theme('colors.marp.brand'),
theme('colors.marp.dark')
);
}
}

2
website/next-env.d.ts vendored Normal file
View File

@ -0,0 +1,2 @@
/// <reference types="next" />
/// <reference types="next/types/global" />

16
website/next.config.js Normal file
View File

@ -0,0 +1,16 @@
const withBundleAnalyzer = require('@next/bundle-analyzer')({
enabled: !!process.env.ANALYZE,
})
const env = { BUILD_YEAR: new Date().getFullYear().toString() }
// for Netlify's deploy preview
if (process.env.DEPLOY_URL) env.NEXT_PUBLIC_HOST = process.env.DEPLOY_URL
module.exports = withBundleAnalyzer({
env,
webpack: (config) => {
config.module.rules.push({ test: /\.md$/, use: 'raw-loader' })
return config
},
})

50
website/package.json Normal file
View File

@ -0,0 +1,50 @@
{
"name": "@marp-team/marp-website",
"version": "0.0.0",
"private": true,
"scripts": {
"check:ts": "tsc --noEmit",
"export": "next build && next export",
"dev": "next dev",
"build": "next build",
"start": "next start"
},
"browser": {
"@marp-team/marp-core": false,
"gray-matter": false,
"remark-parse": false
},
"dependencies": {
"@marp-team/marp-core": "^1.3.0",
"@next/bundle-analyzer": "^9.5.2",
"body-scroll-lock": "^3.0.3",
"classnames": "^2.2.6",
"focus-visible": "^5.1.0",
"github-slugger": "^1.3.0",
"gray-matter": "^4.0.2",
"next": "^9.5.2",
"nprogress": "^0.2.0",
"postcss-flexbugs-fixes": "^4.2.1",
"postcss-preset-env": "^6.7.0",
"prism-react-renderer": "^1.1.1",
"raw-loader": "^4.0.1",
"react": "^16.13.1",
"react-dom": "^16.13.1",
"react-innertext": "^1.1.5",
"remark-parse": "^8.0.3",
"remark-react": "^7.0.1",
"styled-jsx-plugin-postcss": "^2.0.1",
"tailwindcss": "^1.7.3",
"unified": "^9.2.0",
"use-media": "^1.4.0",
"wicg-inert": "^3.0.3"
},
"devDependencies": {
"@types/classnames": "^2.2.10",
"@types/react": "^16.9.46",
"@types/webpack-env": "^1.15.2",
"eslint-plugin-jsx-a11y": "^6.3.1",
"eslint-plugin-react": "^7.20.6",
"eslint-plugin-react-hooks": "^4.1.0"
}
}

43
website/pages/404.tsx Normal file
View File

@ -0,0 +1,43 @@
import { Button } from 'components/Button'
import { Layout } from 'components/Layout'
const error404 = () => (
<Layout title={['404 Not Found']} noIndex>
<section className="text-center">
<div className="w-screen m-8 max-w-2xl sm:m-16">
<h1 className="font-rounded text-4xl text-gray-700 font-bold tracking-tighter">
404 Not Found
</h1>
<hr className="my-6" />
<p className="text-lg">Oops! The requested page could not be found.</p>
<p className="mt-10">
<Button
className="w-full max-w-xs text-xs"
onClick={() => window.history.back()}
outline
style={{ color: '#4a5568' }}
>
<span className="flex justify-center items-center text-lg">
<img
className="w-8 h-8 mr-2"
src="https://icongr.am/octicons/arrow-left.svg?color=4a5568"
alt="←"
/>
Back to previous
</span>
</Button>
</p>
</div>
<style jsx>{`
section {
@apply flex w-full items-center justify-center;
background-image: url("data:image/svg+xml,%3csvg width='40' height='40' xmlns='http://www.w3.org/2000/svg'%3e%3cpath d='M0 40L40 0H20L0 20m40 20V20L20 40' fill='%23f0f0f0' fill-opacity='.2' fill-rule='evenodd'/%3e%3c/svg%3e");
min-height: inherit;
}
`}</style>
</section>
</Layout>
)
export default error404

29
website/pages/_app.tsx Normal file
View File

@ -0,0 +1,29 @@
import { Router } from 'next/router'
import NProgress from 'nprogress'
import 'focus-visible'
import 'wicg-inert'
import 'nprogress/nprogress.css'
import 'css/index.css'
const translating = () => {
NProgress.start()
document.documentElement.classList.add('translating')
}
const translated = () => {
NProgress.done()
setTimeout(
() => document.documentElement.classList.remove('translating'),
250
)
}
Router.events.on('routeChangeStart', translating)
Router.events.on('routeChangeComplete', translated)
Router.events.on('routeChangeError', translated)
NProgress.configure({ showSpinner: false, trickleSpeed: 350 })
const App = ({ Component, pageProps }) => <Component {...pageProps} />
export default App

View File

@ -0,0 +1,26 @@
import Document, { Html, Head, Main, NextScript } from 'next/document'
class MyDocument extends Document {
render = () => (
<Html lang="en">
<Head>
<link rel="icon" href="/favicon.png" type="image/png" />
<link
rel="apple-touch-icon"
sizes="180x180"
href="/apple-touch-icon-180x180.png"
/>
<link
href="https://fonts.googleapis.com/css2?family=Inter:wght@400;600;700&amp;family=Quicksand:wght@500;700&amp;display=swap"
rel="stylesheet"
/>
</Head>
<body>
<Main />
<NextScript />
</body>
</Html>
)
}
export default MyDocument

140
website/pages/blog.tsx Normal file
View File

@ -0,0 +1,140 @@
import { basename } from 'path'
import { InferGetStaticPropsType } from 'next'
import Link from 'next/link'
import { Layout } from 'components/Layout'
import { Title } from 'components/Title'
import { Typography } from 'components/Typography'
import { formatDate, formatDateShort } from 'utils/date'
import { parse, parseMatter, renderToReact } from 'utils/markdown'
export const getStaticProps = async () => {
const ctx = require.context('blog', false, /\.md$/)
const mdMetas = await Promise.all(
ctx.keys().map((id) => {
const { default: md } = ctx(id)
const { data, excerpt } = parseMatter(md)
return (async () => ({
data,
excerpt: excerpt ? await parse(excerpt) : undefined,
slug: basename(id, '.md'),
}))()
})
)
const articles = mdMetas.filter(({ data }) => data.date)
articles.sort((a, b) => b.data.date.getTime() - a.data.date.getTime())
return {
props: {
articles: JSON.parse(JSON.stringify(articles)) as typeof articles,
},
}
}
const Blog = ({ articles }: InferGetStaticPropsType<typeof getStaticProps>) => (
<Layout activeItem="blog" title={['Blog']}>
<Title>
<Link href="/blog">
<a>Blog</a>
</Link>
</Title>
<div className="container mx-auto max-w-screen-lg px-3 py-1">
{articles.map((article) => {
let summary: JSX.Element | string | undefined
if (article.excerpt) {
summary = (
<Typography>{renderToReact(article.excerpt.mdast)}</Typography>
)
} else if (article.data.description) {
summary = article.data.description
}
const date = new Date(article.data.date)
return (
<div key={article.slug} className="article-container">
<Link href={`/blog/${article.slug}`}>
<a className="article-container-link">
<h1 className="sr-only">{article.data.title}</h1>
</a>
</Link>
<div className="relative pointer-events-none">
<h1 className="text-3xl text-gradient font-bold" aria-hidden>
{article.data.title}
</h1>
<p className="mt-2 text-gray-600 pb-4 border-b-2 mb-4 text-sm">
<time dateTime={formatDateShort(date)}>{formatDate(date)}</time>
{article.data.author && ` by ${article.data.author}`}
</p>
{summary && (
<>
{/* @ts-ignore for inert attribute */}
<div className="flex flex-col lg:flex-row" inert="">
{article.data.image && (
<figure className="mx-auto mb-6 lg:order-1 lg:mb-0 lg:ml-6 lg:w-full lg:max-w-sm">
<img
src={article.data.image}
alt={article.data.title}
className="bg-white border border-gray-100 shadow-md w-full max-w-sm"
/>
</figure>
)}
<article className="flex-grow">{summary}</article>
</div>
</>
)}
<p className="read-more">
<img
className="inline w-8 h-8"
src="https://icongr.am/octicons/arrow-right.svg?color=4a5568"
alt="→"
/>
Read more...
</p>
</div>
</div>
)
})}
<style jsx>{`
.article-container {
@apply relative p-6 my-6;
}
.article-container-link {
@apply absolute inset-0 rounded-lg transition-all duration-300;
}
@media not all and (hover: none) {
.article-container-link:hover {
@apply shadow-lg bg-white;
}
.article-container-link:hover:active {
@apply shadow-outline bg-white outline-none;
transition-duration: 0s;
}
.article-container-link:hover + * .read-more {
@apply text-marp-brand;
}
}
.article-container-link:focus {
@apply shadow-outline bg-white outline-none;
transition-duration: 0s;
}
.read-more {
@apply mt-6 text-right uppercase font-bold flex justify-end items-center transition-colors duration-300;
}
@screen md {
.article-container {
@apply relative p-8;
}
}
`}</style>
</div>
</Layout>
)
export default Blog

View File

@ -0,0 +1,69 @@
import { basename } from 'path'
import {
GetStaticPaths,
GetStaticPropsContext,
InferGetStaticPropsType,
} from 'next'
import Link from 'next/link'
import { Layout } from 'components/Layout'
import { Title } from 'components/Title'
import { Typography } from 'components/Typography'
import { BlogHeader } from 'components/blog/BlogHeader'
import { parse, renderToReact } from 'utils/markdown'
export const getStaticPaths: GetStaticPaths = async () => ({
paths: require
.context('blog', false, /\.md$/)
.keys()
.map((id) => `/blog/${basename(id, '.md')}`),
fallback: false,
})
export const getStaticProps = async ({ params }: GetStaticPropsContext) => {
const slug = params?.slug as string
const { default: md } = await import(`blog/${slug}.md`)
return { props: { markdown: await parse(md), slug } }
}
const Blog = ({
markdown,
slug,
}: InferGetStaticPropsType<typeof getStaticProps>) => (
<Layout
activeItem="blog"
description={markdown.data.description || ''}
image={markdown.data.image}
noIndex={!markdown.data.date}
title={[markdown.data.title || slug, 'Blog']}
>
<Title>
<Link href="/blog">
<a>Blog</a>
</Link>
</Title>
<div className="container mx-auto px-6 py-12">
<BlogHeader
title={markdown.data.title}
date={markdown.data.date ? new Date(markdown.data.date) : undefined}
author={markdown.data.author}
github={markdown.data.github}
slug={slug}
/>
<article className="mt-8 mx-auto max-w-screen-lg">
{markdown.data.image && (
<figure className="my-12">
<img
src={markdown.data.image}
alt={markdown.data.title}
className="block bg-white mx-auto max-w-screen-md shadow-xl w-full"
/>
</figure>
)}
<Typography>{renderToReact(markdown.mdast)}</Typography>
</article>
</div>
</Layout>
)
export default Blog

33
website/pages/docs._tsx Normal file
View File

@ -0,0 +1,33 @@
import { Layout } from 'components/docs/Layout'
const Docs = () => (
<Layout breadcrumbs={['Concept', "What's Marp?"]}>
<p>
Lorem ipsum, dolor sit amet consectetur adipisicing elit. Assumenda, earum
porro, ex magnam aut sequi corrupti, minus molestiae neque et maiores
veniam. Odio quos eveniet qui. Non quisquam itaque sunt?
</p>
<p>
Lorem ipsum, dolor sit amet consectetur adipisicing elit. Assumenda, earum
porro, ex magnam aut sequi corrupti, minus molestiae neque et maiores
veniam. Odio quos eveniet qui. Non quisquam itaque sunt?
</p>
<p>
Lorem ipsum, dolor sit amet consectetur adipisicing elit. Assumenda, earum
porro, ex magnam aut sequi corrupti, minus molestiae neque et maiores
veniam. Odio quos eveniet qui. Non quisquam itaque sunt?
</p>
<p>
Lorem ipsum, dolor sit amet consectetur adipisicing elit. Assumenda, earum
porro, ex magnam aut sequi corrupti, minus molestiae neque et maiores
veniam. Odio quos eveniet qui. Non quisquam itaque sunt?
</p>
<p>
Lorem ipsum, dolor sit amet consectetur adipisicing elit. Assumenda, earum
porro, ex magnam aut sequi corrupti, minus molestiae neque et maiores
veniam. Odio quos eveniet qui. Non quisquam itaque sunt?
</p>
</Layout>
)
export default Docs

59
website/pages/index.tsx Normal file
View File

@ -0,0 +1,59 @@
import { InferGetStaticPropsType } from 'next'
import { Layout } from 'components/Layout'
import { generateRenderedMarp } from 'components/Marp'
import { Description } from 'components/top/Description'
import { Features } from 'components/top/Features'
import { GetStarted } from 'components/top/GetStarted'
import { Hero } from 'components/top/Hero'
import { absoluteUrl } from 'utils/url'
const exampleMarkdown = `
---
theme: gaia
_class: lead
paginate: true
backgroundColor: #fff
backgroundImage: url('${absoluteUrl('/assets/hero-background.jpg')}')
---
![bg left:40% 80%](${absoluteUrl('/assets/marp.svg')})
# **Marp**
Markdown Presentation Ecosystem
https://marp.app/
---
# How to write slides
Split pages by horizontal ruler (\`---\`). It's very simple! :satisfied:
\`\`\`markdown
# Slide 1
foobar
---
# Slide 2
foobar
\`\`\`
`.trim()
export const getStaticProps = async () => ({
props: { example: generateRenderedMarp(exampleMarkdown) },
})
const Index = (props: InferGetStaticPropsType<typeof getStaticProps>) => (
<Layout type="website">
<Hero />
<Description example={props.example} />
<Features />
<GetStarted />
</Layout>
)
export default Index

14
website/postcss.config.js Normal file
View File

@ -0,0 +1,14 @@
module.exports = {
plugins: {
'postcss-flexbugs-fixes': {},
tailwindcss: {},
'postcss-preset-env': {
autoprefixer: { flexbox: 'no-2009' },
stage: 3,
features: {
'custom-properties': false,
'focus-visible-pseudo-class': true,
},
},
},
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 371 B

View File

Before

Width:  |  Height:  |  Size: 15 KiB

After

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 52 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 136 KiB

View File

Before

Width:  |  Height:  |  Size: 210 B

After

Width:  |  Height:  |  Size: 210 B

View File

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 522.5 160"><defs><style>.d{fill:none;stroke:#000;stroke-linecap:square;stroke-miterlimit:10;stroke-width:5px}</style></defs><path fill="#67b8e3" d="M250 0l-90 90v70h90V0z"/><path fill="#0288d1" d="M160 0L0 160h90l70-70V0z"/><path fill="#02669d" d="M90 160h70V90l-70 70z"/><path class="d" d="M300 110V50l30 30 30-30v60"/><circle class="d" cx="400" cy="90" r="20"/><path class="d" d="M440 90a20.1 20.1 0 0120-20M420 70v40"/><circle class="d" cx="500" cy="90" r="20"/><path class="d" d="M480 70v60M440 70v40"/></svg>

After

Width:  |  Height:  |  Size: 566 B

View File

Before

Width:  |  Height:  |  Size: 198 B

After

Width:  |  Height:  |  Size: 198 B

View File

Before

Width:  |  Height:  |  Size: 4.6 KiB

After

Width:  |  Height:  |  Size: 4.6 KiB

View File

Before

Width:  |  Height:  |  Size: 24 KiB

After

Width:  |  Height:  |  Size: 24 KiB

BIN
website/public/favicon.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 435 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.6 KiB

View File

@ -0,0 +1,37 @@
const {
colors: { black, current, gray, transparent, white },
fontFamily,
} = require('tailwindcss/defaultTheme')
module.exports = {
future: { removeDeprecatedGapUtilities: true },
plugins: [],
purge: ['@(components|pages|utils)/**/*.[jt]s?(x)'],
theme: {
colors: {
black,
current,
gray,
transparent,
white,
background: '#f8f8f8',
foreground: gray[800],
marp: {
// Brand colors
brand: '#0288d1',
light: '#67b8e3',
dark: '#02669d',
// Color variations
darken: '#0277b7',
darkest: '#1b4d68',
},
},
fontFamily: {
...fontFamily,
sans: ['Inter', ...fontFamily.sans],
rounded: ['Quicksand', 'Avenir', 'Century Gothic', ...fontFamily.sans],
},
},
variants: { transitionDuration: ['responsive', 'hover'] },
}

16
website/tsconfig.json Normal file
View File

@ -0,0 +1,16 @@
{
"extends": "../tsconfig.json",
"compilerOptions": {
"allowJs": true,
"baseUrl": ".",
"isolatedModules": true,
"jsx": "preserve",
"lib": ["dom", "dom.iterable", "esnext"],
"module": "esnext",
"moduleResolution": "node",
"noEmit": true,
"target": "es5"
},
"include": ["next-env.d.ts", "**/*.ts", "**/*.tsx"],
"exclude": ["node_modules"]
}

34
website/utils/date.ts Normal file
View File

@ -0,0 +1,34 @@
const monthNames = [
'January',
'February',
'March',
'April',
'May',
'June',
'July',
'August',
'September',
'October',
'November',
'December',
] as const
const nth = (day) => {
if (day > 3 && day < 21) return `${day}th`
const firstPlace = day % 10
if (firstPlace === 1) return `${day}st`
if (firstPlace === 2) return `${day}nd`
if (firstPlace === 3) return `${day}rd`
return `${day}th`
}
export const formatDate = (date: Date) =>
`${monthNames[date.getMonth()]} ${nth(date.getDate())}, ${date.getFullYear()}`
export const formatDateShort = (date: Date) =>
`${date.getFullYear()}-${`${date.getMonth() + 1}`.padStart(
2,
'0'
)}-${`${date.getDate()}`.padStart(2, '0')}`

138
website/utils/markdown.tsx Normal file
View File

@ -0,0 +1,138 @@
/* eslint-disable jsx-a11y/alt-text, jsx-a11y/anchor-has-content */
import GitHubSlugger from 'github-slugger'
import matter from 'gray-matter'
import gitHubSanitize from 'hast-util-sanitize/lib/github.json'
import Link from 'next/link'
import innerText from 'react-innertext'
import remarkParse from 'remark-parse'
import remarkReact from 'remark-react'
import unified from 'unified'
import { CodeBlock } from 'components/CodeBlock'
const parser = unified().use(remarkParse, { commonmark: true })
const toJSON = (value: any) => JSON.parse(JSON.stringify(value))
export const parseMatter = (markdown: string) =>
matter(markdown, {
excerpt_separator: '<!-- more -->',
})
export const parse = async (markdown: string) => {
const md = parseMatter(markdown)
return {
markdown,
mdast: toJSON(await parser.run(parser.parse(md.content))),
data: toJSON(md.data),
}
}
const Anchor = ({ href, ...rest }) => {
if (href && (href.startsWith('http://') || href.startsWith('https://'))) {
return <a href={href} {...rest} target="_blank" rel="noreferrer noopener" />
}
return (
<Link href={href}>
<a {...rest} />
</Link>
)
}
const MarkWrapper: React.FC = ({ children }) => (
<span>
{children}
<style jsx>{`
& {
box-shadow: inset 0 -0.2em theme('colors.marp.light');
}
`}</style>
</span>
)
const PreCodeBlock: React.FC = (props) => {
if (props['data-code'] === undefined) return <pre {...props} />
return (
<CodeBlock
className="sm:mx-auto sm:w-11/12 lg:w-5/6"
language={props['data-language']}
copyButton
>
{props['data-code']}
</CodeBlock>
)
}
const Img: React.FC = (props) =>
props['title'] ? (
<figure>
<img {...props} />
{props['title'].trim() && <figcaption>{props['title']}</figcaption>}
</figure>
) : (
<img {...props} />
)
export const renderToReact = (
mdast: any,
{ anchorLink = true }: { anchorLink?: boolean } = {}
) => {
const slugger = new GitHubSlugger()
const heading = (level: number, Wrapper?: React.FC) => {
const Heading: React.FC = ({ children }) => {
const anchor = slugger.slug(innerText(children))
const Tag = `h${level}` as any
return (
<Tag>
<a id={anchor} className="named-anchor" aria-hidden />
{anchorLink && (
<a
aria-hidden
className="anchor-link"
href={`#${anchor}`}
tabIndex={-1}
></a>
)}
{Wrapper ? <Wrapper>{children}</Wrapper> : children}
</Tag>
)
}
return Heading
}
const renderer = unified().use(remarkReact, {
remarkReactComponents: {
a: Anchor,
h1: heading(1, MarkWrapper),
h2: heading(2),
h3: heading(3),
h4: heading(4),
h5: heading(5),
h6: heading(6),
pre: PreCodeBlock,
img: Img,
},
sanitize: {
...gitHubSanitize,
attributes: {
...gitHubSanitize.attributes,
'*': [...gitHubSanitize.attributes['*'], 'data*'],
},
},
toHast: {
commonmark: true,
handlers: {
code: (h, { position, lang, value }) =>
h(
position,
'pre',
{ 'data-code': value, 'data-language': lang?.trim() },
[]
),
},
},
})
return renderer.stringify(renderer.runSync(mdast))
}

7
website/utils/title.ts Normal file
View File

@ -0,0 +1,7 @@
export const generateTitle = (breadcrumbs: string[] = []) =>
[
...breadcrumbs,
`Marp${
breadcrumbs.length === 0 ? ': Markdown Presentation Ecosystem' : ''
}`,
].join(' | ')

2
website/utils/url.ts Normal file
View File

@ -0,0 +1,2 @@
export const absoluteUrl = (path: string) =>
new URL(path, process.env.NEXT_PUBLIC_HOST)

6202
yarn.lock

File diff suppressed because it is too large Load Diff