Compare commits
No commits in common. "gh-pages" and "master" have entirely different histories.
14
.editorconfig
Normal file
|
@ -0,0 +1,14 @@
|
|||
# EditorConfig from https://github.com/sivan/dotfiles
|
||||
root = true
|
||||
|
||||
[*]
|
||||
charset = utf-8
|
||||
end_of_line = lf
|
||||
indent_size = 2
|
||||
indent_style = space
|
||||
insert_final_newline = true
|
||||
trim_trailing_whitespace = true
|
||||
|
||||
[*.md]
|
||||
indent_size = 4
|
||||
trim_trailing_whitespace = false
|
1
.github/FUNDING.yml
vendored
Normal file
|
@ -0,0 +1 @@
|
|||
open_collective: heti
|
53
.github/workflows/deploy.yml
vendored
Normal file
|
@ -0,0 +1,53 @@
|
|||
name: deploy
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- master
|
||||
|
||||
jobs:
|
||||
build-and-deploy:
|
||||
runs-on: ubuntu-latest
|
||||
needs: check-env
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v2
|
||||
with:
|
||||
persist-credentials: false
|
||||
- name: Setup Node.js
|
||||
uses: actions/setup-node@v1
|
||||
with:
|
||||
node-version: 18
|
||||
- name: NPM Install and Test
|
||||
run: |
|
||||
npm install
|
||||
npm run test
|
||||
npm run build
|
||||
- name: Publish
|
||||
if: needs.check-env.outputs.check-npm == 'true'
|
||||
uses: JS-DevTools/npm-publish@v1
|
||||
with:
|
||||
token: ${{ secrets.NPM_TOKEN }}
|
||||
- name: Deploy to gh-pages
|
||||
if: needs.check-env.outputs.check-access == 'true'
|
||||
uses: JamesIves/github-pages-deploy-action@releases/v3
|
||||
with:
|
||||
ACCESS_TOKEN: ${{ secrets.ACCESS_TOKEN }}
|
||||
BRANCH: gh-pages
|
||||
FOLDER: _site
|
||||
check-env:
|
||||
runs-on: ubuntu-latest
|
||||
outputs:
|
||||
check-npm: ${{ steps.check-npm.outputs.defined }}
|
||||
check-access: ${{ steps.check-access.outputs.defined }}
|
||||
steps:
|
||||
- id: check-npm
|
||||
env:
|
||||
NPM_TOKEN: ${{ secrets.NPM_TOKEN }}
|
||||
if: ${{ env.NPM_TOKEN != '' }}
|
||||
run: echo "::set-output name=defined::true"
|
||||
- id: check-access
|
||||
env:
|
||||
ACCESS_TOKEN: ${{ secrets.ACCESS_TOKEN }}
|
||||
if: ${{ env.ACCESS_TOKEN != '' }}
|
||||
run: echo "::set-output name=defined::true"
|
31
.github/workflows/nodejs.yml
vendored
Normal file
|
@ -0,0 +1,31 @@
|
|||
---
|
||||
|
||||
name: Node CI
|
||||
|
||||
on: [push, pull_request]
|
||||
|
||||
jobs:
|
||||
build:
|
||||
|
||||
runs-on: ubuntu-20.04
|
||||
|
||||
strategy:
|
||||
matrix:
|
||||
node-version: [16.x, 18.x]
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v1
|
||||
- name: Use Node.js ${{ matrix.node-version }}
|
||||
uses: actions/setup-node@v1
|
||||
with:
|
||||
node-version: ${{ matrix.node-version }}
|
||||
- name: npm install and test
|
||||
run: |
|
||||
npm ci
|
||||
npm test
|
||||
env:
|
||||
CI: true
|
||||
- name: npm build
|
||||
run: npm run build
|
||||
env:
|
||||
CI: true
|
25
.gitignore
vendored
Normal file
|
@ -0,0 +1,25 @@
|
|||
# Editor generate files
|
||||
.idea/
|
||||
.settings/
|
||||
._project/
|
||||
|
||||
# Dev dependencies and cache files
|
||||
node_modules/
|
||||
npm-debug.log
|
||||
|
||||
# Folder view configuration files
|
||||
.DS_Store
|
||||
Desktop.ini
|
||||
|
||||
# Thumbnail cache files
|
||||
*~
|
||||
._*
|
||||
Thumbs.db
|
||||
|
||||
# Files that might appear on external disks
|
||||
.Spotlight-V100
|
||||
.Trashes
|
||||
|
||||
# Dist files
|
||||
dist/
|
||||
umd/
|
21
LICENSE
Normal file
|
@ -0,0 +1,21 @@
|
|||
MIT License
|
||||
|
||||
Copyright (c) 2020 Sivan
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
60
README.md
Normal file
|
@ -0,0 +1,60 @@
|
|||
# 赫蹏
|
||||
|
||||
赫蹏(hètí)是专为中文内容展示设计的排版样式增强。它基于通行的中文排版规范而来,可以为网站的读者带来更好的文章阅读体验。
|
||||
|
||||
预览:[https://sivan.github.io/heti/](https://sivan.github.io/heti/)
|
||||
|
||||
![Preview](https://raw.githubusercontent.com/sivan/heti/master/_site/assets/screenshot-grid.png)
|
||||
|
||||
主要特性:
|
||||
- 贴合网格的排版;
|
||||
- 全标签样式美化;
|
||||
- 预置古文、诗词样式;
|
||||
- 预置多种排版样式(行间注、多栏、竖排等);
|
||||
- 多种预设字体族(仅限桌面端);
|
||||
- 简/繁体中文支持;
|
||||
- 自适应黑暗模式;
|
||||
- 中西文混排美化,不再手敲空格👏(基于 JavaScript 脚本);
|
||||
- 全角标点挤压(基于 JavaScript 脚本);
|
||||
- 兼容 *normalize.css*、*CSS Reset* 等常见样式重置;
|
||||
- 移动端支持;
|
||||
- ……
|
||||
|
||||
总之,用上就会变好看。
|
||||
|
||||
## 使用方法
|
||||
|
||||
1. 在页面的 `<head>` 标签中引入 `heti.css` 文件:
|
||||
```
|
||||
<link rel="stylesheet" href="//unpkg.com/heti/umd/heti.min.css">
|
||||
```
|
||||
1. 在要作用的容器元素上增加 `class="heti"` 的类名即可:
|
||||
```
|
||||
<article class="entry heti">
|
||||
<h1>我的世界观</h1>
|
||||
<p>有钱人的生活就是这么朴实无华,且枯燥</p>
|
||||
……
|
||||
</article>
|
||||
```
|
||||
1. 使用增强脚本(可选):
|
||||
```
|
||||
<script src="//unpkg.com/heti/umd/heti-addon.min.js"></script>
|
||||
<script>
|
||||
const heti = new Heti('.heti');
|
||||
heti.autoSpacing(); // 自动进行中西文混排美化和标点挤压
|
||||
</script>
|
||||
```
|
||||
|
||||
|
||||
## WIP
|
||||
|
||||
暂时没什么想做的了。
|
||||
|
||||
- [x] 自适应黑暗模式
|
||||
- [x] 标点挤压
|
||||
- [x] 中、西文混排
|
||||
- [x] 繁体中文支持
|
||||
- [x] 诗词版式
|
||||
- [x] 行间注版式
|
||||
|
||||
-- EOF --
|
Before Width: | Height: | Size: 77 KiB After Width: | Height: | Size: 77 KiB |
Before Width: | Height: | Size: 132 KiB After Width: | Height: | Size: 132 KiB |
Before Width: | Height: | Size: 133 KiB After Width: | Height: | Size: 133 KiB |
Before Width: | Height: | Size: 12 KiB After Width: | Height: | Size: 12 KiB |
Before Width: | Height: | Size: 5.3 KiB After Width: | Height: | Size: 5.3 KiB |
|
@ -685,7 +685,7 @@
|
|||
const REG_BD_HALF_START = `${REG_BD_HALF_OPEN}${REG_BD_HALF_CLOSE}`;
|
||||
|
||||
class Heti {
|
||||
constructor (rootSelector) {
|
||||
constructor (rootDocument, rootSelector) {
|
||||
let supportLookBehind = true;
|
||||
|
||||
try {
|
||||
|
@ -695,6 +695,7 @@
|
|||
supportLookBehind = false;
|
||||
}
|
||||
|
||||
this.rootDocument = rootDocument;
|
||||
this.rootSelector = rootSelector || '.heti';
|
||||
this.REG_FULL = new RegExp(supportLookBehind ? REG_CJK_FULL : REG_CJK_FULL_WITHOUT_LOOKBEHIND, 'g');
|
||||
this.REG_START = new RegExp(REG_CJK_START, 'g');
|
||||
|
@ -722,8 +723,8 @@
|
|||
forceContext: this.funcForceContext,
|
||||
filterElements: this.funcFilterElements,
|
||||
};
|
||||
const getWrapper = function (elementName, classList, text) {
|
||||
const $$r = document.createElement(elementName);
|
||||
const getWrapper = (elementName, classList, text) => {
|
||||
const $$r = this.rootDocument.createElement(elementName);
|
||||
$$r.className = classList;
|
||||
$$r.textContent = text.trim();
|
||||
return $$r
|
||||
|
@ -768,14 +769,18 @@
|
|||
|
||||
autoSpacing () {
|
||||
const callback = () => {
|
||||
const $$rootList = document.querySelectorAll(this.rootSelector);
|
||||
const $$rootList = this.rootDocument.querySelectorAll(this.rootSelector);
|
||||
|
||||
for (let $$root of $$rootList) {
|
||||
this.spacingElement($$root);
|
||||
}
|
||||
};
|
||||
if (document.readyState === 'complete') setTimeout(callback);
|
||||
else document.addEventListener('DOMContentLoaded', callback);
|
||||
if (this.rootDocument == document) {
|
||||
if (this.rootDocument.readyState === 'complete') setTimeout(callback);
|
||||
else this.rootDocument.addEventListener('DOMContentLoaded', callback);
|
||||
} else {
|
||||
callback();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -167,7 +167,6 @@
|
|||
font-size: 16px;
|
||||
font-weight: 400;
|
||||
-webkit-font-smoothing: subpixel-antialiased;
|
||||
line-height: 1.5;
|
||||
overflow-wrap: break-word;
|
||||
word-wrap: break-word;
|
||||
hyphens: auto;
|
||||
|
@ -190,25 +189,6 @@
|
|||
.heti td > *:last-child {
|
||||
margin-block-end: 0 !important;
|
||||
}
|
||||
.heti blockquote {
|
||||
margin-block-start: 12px;
|
||||
margin-block-end: 24px;
|
||||
margin-inline-start: 32px;
|
||||
margin-inline-end: 32px;
|
||||
padding-block-start: 12px;
|
||||
padding-block-end: 12px;
|
||||
padding-inline-start: 16px;
|
||||
padding-inline-end: 16px;
|
||||
background-color: hsla(0deg, 0%, 0%, 0.054);
|
||||
}
|
||||
[data-darkmode=dark] .heti blockquote {
|
||||
background-color: hsla(0deg, 0%, 100%, 0.054);
|
||||
}
|
||||
@media (prefers-color-scheme: dark) {
|
||||
[data-darkmode=auto] .heti blockquote {
|
||||
background-color: hsla(0deg, 0%, 100%, 0.054);
|
||||
}
|
||||
}
|
||||
.heti figure {
|
||||
display: block;
|
||||
text-align: center;
|
||||
|
@ -221,8 +201,8 @@
|
|||
.heti hr {
|
||||
width: 30%;
|
||||
height: 1px;
|
||||
margin-block-start: 48px;
|
||||
margin-block-end: 47px;
|
||||
margin-block-start: 32px;
|
||||
margin-block-end: 31px;
|
||||
margin-inline-start: auto;
|
||||
margin-inline-end: auto;
|
||||
border: 0;
|
||||
|
@ -237,45 +217,13 @@
|
|||
}
|
||||
}
|
||||
.heti p {
|
||||
margin-block-start: 12px;
|
||||
margin-block-end: 24px;
|
||||
margin-block-start: 8px;
|
||||
margin-block-end: 16px;
|
||||
text-align: justify;
|
||||
}
|
||||
.heti p:not(:lang(zh)):not(:lang(ja)):not(:lang(kr)), .heti p:not(:lang(zh)) {
|
||||
text-align: start;
|
||||
}
|
||||
.heti pre {
|
||||
margin-block-start: 12px;
|
||||
margin-block-end: 12px;
|
||||
margin-inline-start: 0;
|
||||
margin-inline-end: 0;
|
||||
padding-block-start: 12px;
|
||||
padding-block-end: 12px;
|
||||
padding-inline-start: 16px;
|
||||
padding-inline-end: 16px;
|
||||
overflow: auto;
|
||||
font-family: "SFMono-Regular", consolas, "Liberation Mono", menlo, courier, monospace, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol";
|
||||
white-space: pre;
|
||||
word-wrap: normal;
|
||||
border-radius: 4px;
|
||||
background-color: hsla(0deg, 0%, 0%, 0.054);
|
||||
}
|
||||
[data-darkmode=dark] .heti pre {
|
||||
background-color: hsla(0deg, 0%, 100%, 0.054);
|
||||
}
|
||||
@media (prefers-color-scheme: dark) {
|
||||
[data-darkmode=auto] .heti pre {
|
||||
background-color: hsla(0deg, 0%, 100%, 0.054);
|
||||
}
|
||||
}
|
||||
.heti pre code {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
border: 0;
|
||||
border-radius: 0;
|
||||
background-color: transparent;
|
||||
color: inherit;
|
||||
}
|
||||
.heti:not(:lang(zh)):not(:lang(ja)):not(:lang(kr)), .heti:not(:lang(zh)) {
|
||||
letter-spacing: 0;
|
||||
}
|
||||
|
@ -294,12 +242,12 @@
|
|||
.heti h6 {
|
||||
position: relative;
|
||||
margin: 0;
|
||||
margin-block-start: 24px;
|
||||
margin-block-end: 12px;
|
||||
margin-block-start: 16px;
|
||||
margin-block-end: 8px;
|
||||
font-weight: 600;
|
||||
}
|
||||
.heti h1 {
|
||||
margin-block-end: 24px;
|
||||
margin-block-end: 16px;
|
||||
font-size: 32px;
|
||||
line-height: 48px;
|
||||
}
|
||||
|
@ -340,7 +288,7 @@
|
|||
.heti h3 + h4,
|
||||
.heti h4 + h5,
|
||||
.heti h5 + h6 {
|
||||
margin-block-start: 12px;
|
||||
margin-block-start: 8px;
|
||||
}
|
||||
.heti ul,
|
||||
.heti ol,
|
||||
|
@ -359,30 +307,11 @@
|
|||
margin-block-start: 0;
|
||||
margin-block-end: 0;
|
||||
}
|
||||
.heti ul {
|
||||
list-style-type: disc;
|
||||
}
|
||||
.heti ol {
|
||||
list-style-type: decimal;
|
||||
}
|
||||
.heti ul ul,
|
||||
.heti ol ul {
|
||||
list-style-type: circle;
|
||||
}
|
||||
.heti ul ul ul,
|
||||
.heti ul ol ul,
|
||||
.heti ol ul ul,
|
||||
.heti ol ol ul {
|
||||
list-style-type: square;
|
||||
}
|
||||
.heti li {
|
||||
list-style-type: unset;
|
||||
}
|
||||
.heti table {
|
||||
box-sizing: border-box;
|
||||
table-layout: fixed;
|
||||
margin-block-start: 12px;
|
||||
margin-block-end: 24px;
|
||||
margin-block-start: 8px;
|
||||
margin-block-end: 16px;
|
||||
margin-inline-start: auto;
|
||||
margin-inline-end: auto;
|
||||
border-collapse: collapse;
|
||||
|
@ -401,8 +330,8 @@
|
|||
}
|
||||
.heti th,
|
||||
.heti td {
|
||||
padding-block-start: 6px;
|
||||
padding-block-end: 6px;
|
||||
padding-block-start: 4px;
|
||||
padding-block-end: 4px;
|
||||
padding-inline-start: 8px;
|
||||
padding-inline-end: 8px;
|
||||
border-width: 1px;
|
||||
|
@ -444,12 +373,6 @@
|
|||
.heti strong {
|
||||
font-weight: 600;
|
||||
}
|
||||
.heti code {
|
||||
margin-inline-start: 0.25em;
|
||||
margin-inline-end: 0.25em;
|
||||
font-family: "SFMono-Regular", consolas, "Liberation Mono", menlo, courier, monospace, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol";
|
||||
font-size: 0.875em;
|
||||
}
|
||||
.heti dfn {
|
||||
font-weight: 600;
|
||||
}
|
||||
|
@ -493,7 +416,7 @@
|
|||
}
|
||||
}
|
||||
.heti q {
|
||||
quotes: "「" "」" "『" "』";
|
||||
quotes: "“" "”" "‘" "’";
|
||||
}
|
||||
.heti q:not(:lang(zh)):not(:lang(ja)):not(:lang(kr)), .heti q:not(:lang(zh)) {
|
||||
quotes: initial;
|
||||
|
@ -631,8 +554,8 @@
|
|||
column-gap: 2em;
|
||||
}
|
||||
.heti--columns-1 p, .heti--columns-2 p, .heti--columns-3 p, .heti--columns-4 p, .heti--columns-16em p, .heti--columns-20em p, .heti--columns-24em p, .heti--columns-28em p, .heti--columns-32em p, .heti--columns-36em p, .heti--columns-40em p, .heti--columns-44em p, .heti--columns-48em p, .heti comma p {
|
||||
margin-block-start: 6px;
|
||||
margin-block-end: 12px;
|
||||
margin-block-start: 4px;
|
||||
margin-block-end: 8px;
|
||||
text-indent: 2em;
|
||||
}
|
||||
.heti--columns-1 {
|
||||
|
@ -689,7 +612,7 @@
|
|||
text-align: start;
|
||||
}
|
||||
.heti--vertical q {
|
||||
quotes: "「" "」" "『" "』";
|
||||
quotes: "『" "』" "「" "」";
|
||||
}
|
||||
.heti--ancient, .heti--poetry {
|
||||
font-family: "Times New Roman", times, "Heti Song", serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol";
|
||||
|
@ -772,8 +695,8 @@
|
|||
text-emphasis: none;
|
||||
}
|
||||
.heti--annotation .heti-meta {
|
||||
margin-block-start: 12px;
|
||||
margin-block-end: 24px;
|
||||
margin-block-start: 8px;
|
||||
margin-block-end: 16px;
|
||||
}
|
||||
.heti .heti-meta {
|
||||
display: block;
|
||||
|
@ -835,7 +758,7 @@
|
|||
}
|
||||
}
|
||||
.heti .heti-fn ol {
|
||||
margin-block-start: 12px;
|
||||
margin-block-start: 8px;
|
||||
margin-block-end: 0;
|
||||
}
|
||||
.heti .heti-fn li:target {
|
|
@ -622,7 +622,7 @@
|
|||
$$article.className = ['article', 'heti', e.target.value].join(' ')
|
||||
})
|
||||
|
||||
const heti = new Heti('.article')
|
||||
const heti = new Heti(document, '.article')
|
||||
heti.autoSpacing()
|
||||
</script>
|
||||
</body>
|
138
js/heti-addon.js
Normal file
|
@ -0,0 +1,138 @@
|
|||
/**
|
||||
* Heti add-on v 0.1.0
|
||||
* Add right spacing between CJK & ANS characters
|
||||
*/
|
||||
|
||||
import Finder from 'heti-findandreplacedomtext'
|
||||
|
||||
const hasOwn = {}.hasOwnProperty
|
||||
const HETI_NON_CONTIGUOUS_ELEMENTS = Object.assign({}, Finder.NON_CONTIGUOUS_PROSE_ELEMENTS, {
|
||||
ins: 1, del: 1, s: 1, a: 1,
|
||||
})
|
||||
const HETI_SKIPPED_ELEMENTS = Object.assign({}, Finder.NON_PROSE_ELEMENTS, {
|
||||
pre: 1, code: 1, sup: 1, sub: 1, 'heti-spacing': 1, 'heti-close': 1,
|
||||
})
|
||||
const HETI_SKIPPED_CLASS = 'heti-skip'
|
||||
|
||||
// 部分正则表达式修改自 pangu.js https://github.com/vinta/pangu.js
|
||||
const CJK = '\u2e80-\u2eff\u2f00-\u2fdf\u3040-\u309f\u30a0-\u30fa\u30fc-\u30ff\u3100-\u312f\u3200-\u32ff\u3400-\u4dbf\u4e00-\u9fff\uf900-\ufaff'
|
||||
const A = 'A-Za-z\u0080-\u00ff\u0370-\u03ff'
|
||||
const N = '0-9'
|
||||
const S = '`~!@#\\$%\\^&\\*\\(\\)-_=\\+\\[\\]{}\\\\\\|;:\'",<.>\\/\\?'
|
||||
const ANS = `${A}${N}${S}`
|
||||
const REG_CJK_FULL = `(?<=[${CJK}])( *[${ANS}]+(?: +[${ANS}]+)* *)(?=[${CJK}])`
|
||||
const REG_CJK_START = `([${ANS}]+(?: +[${ANS}]+)* *)(?=[${CJK}])`
|
||||
const REG_CJK_END = `(?<=[${CJK}])( *[${ANS}]+(?: +[${ANS}]+)*)`
|
||||
const REG_CJK_FULL_WITHOUT_LOOKBEHIND = `(?:[${CJK}])( *[${ANS}]+(?: +[${ANS}]+)* *)(?=[${CJK}])`
|
||||
const REG_CJK_END_WITHOUT_LOOKBEHIND = `(?:[${CJK}])( *[${ANS}]+(?: +[${ANS}]+)*)`
|
||||
const REG_BD_STOP = `。.,、:;!‼?⁇`
|
||||
const REG_BD_SEP = `·・‧`
|
||||
const REG_BD_OPEN = `「『(《〈【〖〔[{`
|
||||
const REG_BD_CLOSE = `」』)》〉】〗〕]}`
|
||||
const REG_BD_START = `${REG_BD_OPEN}${REG_BD_CLOSE}`
|
||||
const REG_BD_END = `${REG_BD_STOP}${REG_BD_OPEN}${REG_BD_CLOSE}`
|
||||
const REG_BD_HALF_OPEN = `“‘`
|
||||
const REG_BD_HALF_CLOSE = `”’`
|
||||
const REG_BD_HALF_START = `${REG_BD_HALF_OPEN}${REG_BD_HALF_CLOSE}`
|
||||
|
||||
class Heti {
|
||||
constructor (rootDocument, rootSelector) {
|
||||
let supportLookBehind = true
|
||||
|
||||
try {
|
||||
new RegExp(`(?<=\d)\d`, 'g').test('')
|
||||
} catch (err) {
|
||||
console.info(err.name, '该浏览器尚未实现 RegExp positive lookbehind')
|
||||
supportLookBehind = false
|
||||
}
|
||||
|
||||
this.rootDocument = rootDocument
|
||||
this.rootSelector = rootSelector || '.heti'
|
||||
this.REG_FULL = new RegExp(supportLookBehind ? REG_CJK_FULL : REG_CJK_FULL_WITHOUT_LOOKBEHIND, 'g')
|
||||
this.REG_START = new RegExp(REG_CJK_START, 'g')
|
||||
this.REG_END = new RegExp(supportLookBehind ? REG_CJK_END : REG_CJK_END_WITHOUT_LOOKBEHIND, 'g')
|
||||
this.offsetWidth = supportLookBehind ? 0 : 1
|
||||
this.funcForceContext = function forceContext (el) {
|
||||
return hasOwn.call(HETI_NON_CONTIGUOUS_ELEMENTS, el.nodeName.toLowerCase())
|
||||
}
|
||||
this.funcFilterElements = function filterElements (el) {
|
||||
return (
|
||||
!(el.classList && el.classList.contains(HETI_SKIPPED_CLASS)) &&
|
||||
!hasOwn.call(HETI_SKIPPED_ELEMENTS, el.nodeName.toLowerCase())
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
spacingElements (elmList) {
|
||||
for (let $$root of elmList) {
|
||||
this.spacingElement($$root)
|
||||
}
|
||||
}
|
||||
|
||||
spacingElement ($$elm) {
|
||||
const commonConfig = {
|
||||
forceContext: this.funcForceContext,
|
||||
filterElements: this.funcFilterElements,
|
||||
}
|
||||
const getWrapper = (elementName, classList, text) => {
|
||||
const $$r = this.rootDocument.createElement(elementName)
|
||||
$$r.className = classList
|
||||
$$r.textContent = text.trim()
|
||||
return $$r
|
||||
}
|
||||
|
||||
Finder($$elm, Object.assign({}, commonConfig, {
|
||||
find: this.REG_FULL,
|
||||
replace: portion => getWrapper('heti-spacing', 'heti-spacing-start heti-spacing-end', portion.text),
|
||||
offset: this.offsetWidth,
|
||||
}))
|
||||
|
||||
Finder($$elm, Object.assign({}, commonConfig, {
|
||||
find: this.REG_START,
|
||||
replace: portion => getWrapper('heti-spacing', 'heti-spacing-start', portion.text),
|
||||
}))
|
||||
|
||||
Finder($$elm, Object.assign({}, commonConfig, {
|
||||
find: this.REG_END,
|
||||
replace: portion => getWrapper('heti-spacing', 'heti-spacing-end', portion.text),
|
||||
offset: this.offsetWidth,
|
||||
}))
|
||||
|
||||
Finder($$elm, Object.assign({}, commonConfig, {
|
||||
find: new RegExp(`([${REG_BD_STOP}])(?=[${REG_BD_START}])|([${REG_BD_OPEN}])(?=[${REG_BD_OPEN}])|([${REG_BD_CLOSE}])(?=[${REG_BD_END}])`,'g'),
|
||||
replace: portion => getWrapper('heti-adjacent', 'heti-adjacent-half', portion.text),
|
||||
offset: this.offsetWidth,
|
||||
}))
|
||||
|
||||
Finder($$elm, Object.assign({}, commonConfig, {
|
||||
find: new RegExp(`([${REG_BD_SEP}])(?=[${REG_BD_OPEN}])|([${REG_BD_CLOSE}])(?=[${REG_BD_SEP}])`,'g'),
|
||||
replace: portion => getWrapper('heti-adjacent', 'heti-adjacent-quarter', portion.text),
|
||||
offset: this.offsetWidth,
|
||||
}))
|
||||
|
||||
// 使用弯引号的情况下,在停顿符号接弯引号(如「。“」)或弯引号接全角开引号(如“《」)时,间距缩进调整到四分之一
|
||||
Finder($$elm, Object.assign({}, commonConfig, {
|
||||
find: new RegExp(`([${REG_BD_STOP}])(?=[${REG_BD_HALF_START}])|([${REG_BD_HALF_OPEN}])(?=[${REG_BD_OPEN}])`,'g'),
|
||||
replace: portion => getWrapper('heti-adjacent', 'heti-adjacent-quarter', portion.text),
|
||||
offset: this.offsetWidth,
|
||||
}))
|
||||
}
|
||||
|
||||
autoSpacing () {
|
||||
const callback = () => {
|
||||
const $$rootList = this.rootDocument.querySelectorAll(this.rootSelector)
|
||||
|
||||
for (let $$root of $$rootList) {
|
||||
this.spacingElement($$root)
|
||||
}
|
||||
}
|
||||
if (this.rootDocument == document) {
|
||||
if (this.rootDocument.readyState === 'complete') setTimeout(callback)
|
||||
else this.rootDocument.addEventListener('DOMContentLoaded', callback)
|
||||
} else {
|
||||
callback()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export default Heti
|
72
lib/_base.scss
Normal file
|
@ -0,0 +1,72 @@
|
|||
// Author: Sivan [sun.sivan@gmail.com]
|
||||
// Description: base reset and entry styles.
|
||||
@import "variables";
|
||||
|
||||
@mixin hetiBase {
|
||||
// 清容器浮动
|
||||
@include clear-float();
|
||||
|
||||
// 清容器内首尾元素外边距
|
||||
&,
|
||||
section,
|
||||
td {
|
||||
> *:first-child {
|
||||
margin-block-start: 0 !important;
|
||||
}
|
||||
|
||||
> *:last-child {
|
||||
margin-block-end: 0 !important;
|
||||
}
|
||||
}
|
||||
|
||||
figure {
|
||||
display: block;
|
||||
text-align: center;
|
||||
|
||||
> img {
|
||||
display: block;
|
||||
margin-inline-start: auto;
|
||||
margin-inline-end: auto;
|
||||
}
|
||||
}
|
||||
|
||||
hr {
|
||||
width: 30%;
|
||||
height: 1px;
|
||||
margin-block-start: $std-block-unit * 2;
|
||||
margin-block-end: $std-block-unit * 2 - 1px;
|
||||
margin-inline-start: auto;
|
||||
margin-inline-end: auto;
|
||||
border: 0;
|
||||
background-color: hsl(0, 0%, 80%);
|
||||
|
||||
@include darkmode-style {
|
||||
background-color: hsl(0, 0%, 25%);
|
||||
}
|
||||
}
|
||||
|
||||
p {
|
||||
margin-block-start: $std-block-unit * 0.5;
|
||||
margin-block-end: $std-block-unit;
|
||||
text-align: justify;
|
||||
|
||||
@include non-cjk-block {
|
||||
text-align: start;
|
||||
}
|
||||
}
|
||||
|
||||
// 非中文时不加间距
|
||||
letter-spacing: $letter-spacing-medium;
|
||||
@include non-cjk-block {
|
||||
letter-spacing: $letter-spacing-normal;
|
||||
}
|
||||
|
||||
a,
|
||||
abbr,
|
||||
code,
|
||||
heti-spacing,
|
||||
[lang="en-US"] {
|
||||
// There should be no leeter-spacing between English characters.
|
||||
letter-spacing: normal;
|
||||
}
|
||||
}
|
5
lib/_font.scss
Normal file
|
@ -0,0 +1,5 @@
|
|||
// Author: Sivan [sun.sivan@gmail.com]
|
||||
// Description: define font-face.
|
||||
@import "fonts/hei";
|
||||
@import "fonts/song";
|
||||
@import "fonts/kai";
|
73
lib/_heading.scss
Normal file
|
@ -0,0 +1,73 @@
|
|||
// Author: Sivan [sun.sivan@gmail.com]
|
||||
// Description: heading styles.
|
||||
@import "variables";
|
||||
|
||||
@mixin hetiHeading {
|
||||
h1,
|
||||
h2,
|
||||
h3,
|
||||
h4,
|
||||
h5,
|
||||
h6 {
|
||||
position: relative;
|
||||
|
||||
// 顶边距默认为一行间距,且不因边距重叠原因减半
|
||||
// 底边距考虑到亲密性,默认为半行间距
|
||||
margin: 0;
|
||||
margin-block-start: $std-block-unit;
|
||||
margin-block-end: $std-block-unit * 0.5;
|
||||
font-weight: $font-weight-bold;
|
||||
}
|
||||
|
||||
h1 {
|
||||
margin-block-end: $std-block-unit;
|
||||
font-size: $font-size-h1;
|
||||
line-height: $line-height-size-h1;
|
||||
}
|
||||
|
||||
h2 {
|
||||
font-size: $font-size-h2;
|
||||
line-height: $line-height-size-h2;
|
||||
}
|
||||
|
||||
h3 {
|
||||
font-size: $font-size-h3;
|
||||
line-height: $line-height-size-h3;
|
||||
}
|
||||
|
||||
h4 {
|
||||
font-size: $font-size-h4;
|
||||
line-height: $line-height-size-h4;
|
||||
}
|
||||
|
||||
h5 {
|
||||
font-size: $font-size-h5;
|
||||
line-height: $line-height-size-h5;
|
||||
}
|
||||
|
||||
h6 {
|
||||
font-size: $font-size-h6;
|
||||
line-height: $line-height-size-h6;
|
||||
}
|
||||
|
||||
h1,
|
||||
h2,
|
||||
h3 {
|
||||
// 中文大标题增加微小文字间距
|
||||
letter-spacing: 0.05em;
|
||||
|
||||
// 非中文时不加间距
|
||||
@include non-cjk-block {
|
||||
letter-spacing: 0;
|
||||
}
|
||||
}
|
||||
|
||||
// 压缩两个标题之间的间距
|
||||
h1 + h2,
|
||||
h2 + h3,
|
||||
h3 + h4,
|
||||
h4 + h5,
|
||||
h5 + h6 {
|
||||
margin-block-start: $std-block-unit * 0.5;
|
||||
}
|
||||
}
|
189
lib/_inline.scss
Normal file
|
@ -0,0 +1,189 @@
|
|||
// Author: Sivan [sun.sivan@gmail.com]
|
||||
// Description: inline element styles.
|
||||
@import "variables";
|
||||
|
||||
@mixin hetiInline {
|
||||
a {
|
||||
text-decoration: none;
|
||||
|
||||
&:hover {
|
||||
padding-block-end: 1px;
|
||||
border-block-end: 1px solid currentColor;
|
||||
text-decoration: none;
|
||||
}
|
||||
}
|
||||
|
||||
abbr[title] {
|
||||
padding-block-end: 1px;
|
||||
border-block-end: 1px dotted;
|
||||
text-decoration: none;
|
||||
cursor: help;
|
||||
}
|
||||
|
||||
b,
|
||||
strong {
|
||||
font-weight: $font-weight-bold;
|
||||
}
|
||||
|
||||
dfn {
|
||||
font-weight: $font-weight-bold;
|
||||
|
||||
// 非中文时不加粗
|
||||
@include non-cjk-block {
|
||||
font-weight: $font-weight-normal;
|
||||
}
|
||||
}
|
||||
|
||||
em {
|
||||
font-weight: $font-weight-bold;
|
||||
}
|
||||
|
||||
// 标题单行时居中,多行时居左
|
||||
figcaption {
|
||||
display: inline-block;
|
||||
vertical-align: top;
|
||||
font-size: $font-size-small;
|
||||
text-align: start;
|
||||
}
|
||||
|
||||
// 显式斜体标签予以保留
|
||||
i {
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
ins,
|
||||
u {
|
||||
padding-block-end: 1px;
|
||||
border-block-end: 1px solid;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
mark {
|
||||
padding-block-start: 2px;
|
||||
padding-block-end: 2px;
|
||||
padding-inline-start: 1px;
|
||||
padding-inline-end: 1px;
|
||||
margin-inline-start: 1px;
|
||||
margin-inline-end: 1px;
|
||||
background-color: hsla(58, 100%, 50%, 0.88);
|
||||
color: inherit;
|
||||
|
||||
@include darkmode-style {
|
||||
background-color: hsla(58, 100%, 15%, 0.88);
|
||||
}
|
||||
}
|
||||
|
||||
// 设置引用文本为中文引号
|
||||
// 默认无论简繁都采用台湾规范,修改只需重新定义变量 `$chinese-quote-set` 即可。
|
||||
q {
|
||||
quotes: map-get(map-get($chinese-quote-presets, $chinese-quote-set), "horizontal");
|
||||
|
||||
@include non-cjk-block {
|
||||
quotes: initial;
|
||||
quotes: auto;
|
||||
}
|
||||
}
|
||||
|
||||
rt {
|
||||
font-size: 0.875em;
|
||||
font-weight: $font-weight-normal;
|
||||
}
|
||||
|
||||
// 完美 <small> 字号 by Sivan
|
||||
/// 12px * 0.875 => 11px
|
||||
/// 14px * 0.875 => 12px
|
||||
/// 16px * 0.875 => 14px
|
||||
/// 18px * 0.875 => 16px
|
||||
/// 20px * 0.875 => 18px
|
||||
small {
|
||||
font-size: 0.875em;
|
||||
}
|
||||
|
||||
strong {
|
||||
font-weight: $font-weight-bold;
|
||||
}
|
||||
|
||||
sub,
|
||||
sup {
|
||||
position: relative;
|
||||
margin-inline-start: 0.25em;
|
||||
margin-inline-end: 0.25em;
|
||||
font-size: 0.75em;
|
||||
font-family: $font-family-hei;
|
||||
font-style: normal;
|
||||
line-height: 1;
|
||||
vertical-align: baseline;
|
||||
}
|
||||
|
||||
sub {
|
||||
bottom: -0.25em;
|
||||
}
|
||||
|
||||
sup {
|
||||
top: -0.5em;
|
||||
|
||||
&:target,
|
||||
a:target {
|
||||
background-color: hsl(210, 100%, 93%);
|
||||
|
||||
@include darkmode-style {
|
||||
background-color: hsl(210, 40%, 38%);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
summary {
|
||||
padding-inline-start: 1em;
|
||||
outline: 0;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
summary::-webkit-details-marker {
|
||||
width: 0.6em;
|
||||
margin-inline-end: 0.4em;
|
||||
}
|
||||
|
||||
u[title] {
|
||||
cursor: help;
|
||||
border-block-end-width: 3px;
|
||||
border-block-end-style: double;
|
||||
border-block-end-color: hsla(0, 0%, 0%, 0.54);
|
||||
|
||||
@include darkmode-style {
|
||||
border-block-end-color: hsla(0, 0%, 100%, 0.54);
|
||||
}
|
||||
}
|
||||
|
||||
// 默认禁用中文斜体 https://www.zhihu.com/question/20120243
|
||||
address,
|
||||
cite,
|
||||
dfn,
|
||||
dt,
|
||||
em {
|
||||
font-style: normal;
|
||||
|
||||
// 非中文时显示斜体
|
||||
@include non-cjk-block {
|
||||
font-style: italic;
|
||||
}
|
||||
}
|
||||
|
||||
// 为带划线的元素添加间距,以防止视觉上混为一个元素
|
||||
// 注: 如果设成为两个相连元素添加间距,会有一个问题:
|
||||
// 如果结构是 `<u>倚天</u>和<u>屠龙</u>`,「屠龙」前面仍然会有边距。
|
||||
// 此处跟预期不一致,应该只在两个同名元素紧邻时增加边距,即:`<u>倚天剑</u><u>屠龙刀</u>`
|
||||
//@each $tag in (abbr[title], del, ins, s, u) {
|
||||
// #{$tag} + #{$tag} {
|
||||
// margin-inline-start: 0.125em;
|
||||
// }
|
||||
//}
|
||||
// 因此采用下面的形式,为所有加划线的元素增加缝隙
|
||||
abbr[title],
|
||||
del,
|
||||
ins,
|
||||
s,
|
||||
u {
|
||||
margin-inline-start: 1px;
|
||||
margin-inline-end: 1px;
|
||||
}
|
||||
}
|
54
lib/_list.scss
Normal file
|
@ -0,0 +1,54 @@
|
|||
// Author: Sivan [sun.sivan@gmail.com]
|
||||
// Description: list styles.
|
||||
@import "variables";
|
||||
|
||||
@mixin hetiList {
|
||||
// 标准化间距
|
||||
ul,
|
||||
ol,
|
||||
dl {
|
||||
margin-block-start: $line-height-size-normal * 0.5;
|
||||
margin-block-end: $line-height-size-normal;
|
||||
}
|
||||
|
||||
ul,
|
||||
ol {
|
||||
padding-inline-start: $text-indent-size;
|
||||
|
||||
ul,
|
||||
ol {
|
||||
margin-block-start: 0;
|
||||
margin-block-end: 0;
|
||||
}
|
||||
}
|
||||
|
||||
// 兼容性处理
|
||||
/// 重置部分 CSS Reset 中 ul, ol { list-style: none; } 造成的样式污染
|
||||
/// 如果搭配 normalize.css 使用,则不存在这些样式污染
|
||||
@if $_css-reset-scheme == "reset" {
|
||||
ul {
|
||||
list-style-type: disc;
|
||||
}
|
||||
|
||||
ol {
|
||||
list-style-type: decimal;
|
||||
}
|
||||
|
||||
ul ul,
|
||||
ol ul {
|
||||
list-style-type: circle;
|
||||
}
|
||||
|
||||
ul ul ul,
|
||||
ul ol ul,
|
||||
ol ul ul,
|
||||
ol ol ul {
|
||||
list-style-type: square;
|
||||
}
|
||||
|
||||
// 重置不知道哪里散播出来的垃圾代码 ul, li { list-style: none; }
|
||||
li {
|
||||
list-style-type: unset;
|
||||
}
|
||||
}
|
||||
}
|
46
lib/_table.scss
Normal file
|
@ -0,0 +1,46 @@
|
|||
// Author: Sivan [sun.sivan@gmail.com]
|
||||
// Description: table styles.
|
||||
@import "variables";
|
||||
|
||||
@mixin hetiTable {
|
||||
table {
|
||||
box-sizing: border-box;
|
||||
table-layout: fixed;
|
||||
margin-block-start: $std-block-unit * 0.5;
|
||||
margin-block-end: $std-block-unit;
|
||||
margin-inline-start: auto;
|
||||
margin-inline-end: auto;
|
||||
border-collapse: collapse;
|
||||
border-width: 1px;
|
||||
border-style: solid;
|
||||
border-color: hsl(0, 0%, 80%);
|
||||
word-break: break-word;
|
||||
|
||||
@include darkmode-style {
|
||||
border-color: hsl(0, 0%, 25%);
|
||||
}
|
||||
}
|
||||
|
||||
th,
|
||||
td {
|
||||
padding-block-start: $std-block-unit * 0.25;
|
||||
padding-block-end: $std-block-unit * 0.25;
|
||||
padding-inline-start: $std-inline-unit * 0.5;
|
||||
padding-inline-end: $std-inline-unit * 0.5;
|
||||
border-width: 1px;
|
||||
border-style: solid;
|
||||
border-color: hsl(0, 0%, 80%);
|
||||
|
||||
@include darkmode-style {
|
||||
border-color: hsl(0, 0%, 25%);
|
||||
}
|
||||
}
|
||||
|
||||
caption {
|
||||
caption-side: bottom;
|
||||
margin-block-start: 2px;
|
||||
margin-block-end: -4px;
|
||||
font-size: $font-size-small;
|
||||
line-height: $line-height-size-small;
|
||||
}
|
||||
}
|
178
lib/_variables.scss
Normal file
|
@ -0,0 +1,178 @@
|
|||
// Author: Sivan [sun.sivan@gmail.com]
|
||||
// Description: define variables, alias etc.
|
||||
|
||||
// 定义赫蹏根 class 名
|
||||
$root-selector: '.heti' !default;
|
||||
$darkmode: true !default; // true | false | 'manual'
|
||||
$manualmode-auto-selector: '[data-darkmode="auto"] &' !default;
|
||||
$manualmode-dark-selector: '[data-darkmode="dark"] &' !default;
|
||||
|
||||
// 字体 Fonts
|
||||
// 字体栈 Font Stacks
|
||||
$_font-stack-sans: "Helvetica Neue", helvetica, arial !default;
|
||||
$_font-stack-serif: "Times New Roman", times !default;
|
||||
$_font-stack-mono: "SFMono-Regular", consolas, "Liberation Mono", menlo, courier !default;
|
||||
$_font-stack-symbol: "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol" !default;
|
||||
|
||||
// 字体族 Font Families
|
||||
$font-family-hei: $_font-stack-sans, "Heti Hei", sans-serif, $_font-stack-symbol !default;
|
||||
$font-family-song: $_font-stack-serif, "Heti Song", serif, $_font-stack-symbol !default;
|
||||
$font-family-kai: $_font-stack-serif, "Heti Kai", serif, $_font-stack-symbol !default;
|
||||
$font-family-hei-black: $_font-stack-sans, "Heti Hei Black", sans-serif, $_font-stack-symbol !default;
|
||||
$font-family-song-black: $_font-stack-serif, "Heti Song Black", serif, $_font-stack-symbol !default;
|
||||
$font-family-kai-black: $_font-stack-serif, "Heti Kai Black", serif, $_font-stack-symbol !default;
|
||||
$font-family-mono: $_font-stack-mono, monospace, $_font-stack-symbol !default;
|
||||
|
||||
// 字重 Font Weights
|
||||
$font-weight-bolder: 800 !default;
|
||||
$font-weight-bold: 600 !default;
|
||||
$font-weight-normal: 400 !default;
|
||||
$font-weight-lighter: 200 !default;
|
||||
|
||||
// 字号 Font Sizes
|
||||
$font-size-normal: 16px !default;
|
||||
$font-size-x-large: 20px !default;
|
||||
$font-size-large: 18px !default;
|
||||
$font-size-small: 14px !default;
|
||||
$font-size-x-small: 12px !default;
|
||||
|
||||
$font-size-h1: 32px !default;
|
||||
$font-size-h2: 24px !default;
|
||||
$font-size-h3: 20px !default;
|
||||
$font-size-h4: 18px !default;
|
||||
$font-size-h5: 16px !default;
|
||||
$font-size-h6: 14px !default;
|
||||
|
||||
// 行 Lines
|
||||
// 行宽
|
||||
$line-length: 42em !default;
|
||||
|
||||
// 行高 Line Heights
|
||||
$line-height-normal: 1.5 !default;
|
||||
//$line-height-expanded: 1.875 !default;
|
||||
$line-height-expanded-ultra: 2.25 !default;
|
||||
//$line-height-condensed: 1.25 !default;
|
||||
//$line-height-condensed-ultra: 1 !default;
|
||||
|
||||
// 字符间距
|
||||
$letter-spacing-normal: 0 !default;
|
||||
$letter-spacing-small: 0.01em !default;
|
||||
$letter-spacing-medium: 0.02em !default;
|
||||
$letter-spacing-large: 0.05em !default;
|
||||
|
||||
$line-height-size-normal: $font-size-normal * $line-height-normal !default;
|
||||
$line-height-size-large: $line-height-size-normal !default;
|
||||
$line-height-size-x-large: $font-size-x-large * $line-height-normal !default;
|
||||
$line-height-size-small: $line-height-size-normal !default;
|
||||
$line-height-size-x-small: 18px !default;
|
||||
$line-height-size-h1: 48px !default;
|
||||
$line-height-size-h2: 36px !default;
|
||||
$line-height-size-h3: 36px !default;
|
||||
$line-height-size-h4: 24px !default;
|
||||
$line-height-size-h5: 24px !default;
|
||||
$line-height-size-h6: 24px !default;
|
||||
|
||||
// 段落 Paragraphs
|
||||
// 标准网格单位变量 Standard Length
|
||||
// 垂直方向标准单位 = 标准行高
|
||||
// 水平方向标准单位 = 标准字号
|
||||
$std-block-unit: 16px;
|
||||
$std-inline-unit: $font-size-normal !default;
|
||||
|
||||
// 示例:缩进单位 = 二倍文字宽度
|
||||
$text-indent-length: 2em !default;
|
||||
$text-indent-size: $font-size-normal * 2 !default;
|
||||
|
||||
// 中文引号 Chinese Quote Set
|
||||
// `cn`:中华人民共和国国家标准——GB/T 15834-2011《标点符号用法》 http://www.moe.gov.cn/ewebeditor/uploadfile/2015/01/13/20150113091548267.pdf
|
||||
// `tw`:中国台湾地区标准——《重訂標點符號手冊》 https://language.moe.gov.tw/001/Upload/FILES/SITE_CONTENT/M0001/HAU/h6.htm
|
||||
// `common`:部分中文社区(如知乎)在简体中文里亦采用与中国台湾地区标准一致的规范。
|
||||
// 注:垂直时浏览器会自动旋转,无需定义为垂直方向的字符。
|
||||
$chinese-quote-presets: (
|
||||
"cn": (
|
||||
"horizontal": "“" "”" "‘" "’",
|
||||
"vertical": "『" "』" "「" "」"
|
||||
),
|
||||
"tw": (
|
||||
"horizontal": "「" "」" "『" "』",
|
||||
"vertical": "「" "」" "『" "』"
|
||||
),
|
||||
"common": (
|
||||
"horizontal": "「" "」" "『" "』",
|
||||
"vertical": "「" "」" "『" "』"
|
||||
)
|
||||
) !default;
|
||||
|
||||
$chinese-quote-set: "cn" !default;
|
||||
|
||||
// 栏 Columns
|
||||
// 分栏
|
||||
$column-count-list: (1, 2, 3, 4) !default;
|
||||
$column-width-list: (16em, 20em, 24em, 28em, 32em, 36em, 40em, 44em, 48em) !default;
|
||||
|
||||
// 开发用配置项 Develop Configs
|
||||
/// 预设重置方案
|
||||
/// `reset`:假定 Eric Meyer 的 CSS Reset 或其它流行的 Reset
|
||||
/// `normalize`:指定为 normalize.css
|
||||
$_css-reset-scheme: "normalize";
|
||||
|
||||
// 混合 Mix-ins
|
||||
// Mix-in: Clear float
|
||||
@mixin clear-float {
|
||||
&::before,
|
||||
&::after {
|
||||
content: "";
|
||||
display: table;
|
||||
}
|
||||
|
||||
&::after {
|
||||
clear: both;
|
||||
}
|
||||
}
|
||||
|
||||
// Mix-in: Include Non-cjk styles
|
||||
@mixin non-cjk-block {
|
||||
&:not(:lang(zh)):not(:lang(ja)):not(:lang(kr)),
|
||||
&:not(:lang(zh)) {
|
||||
@content;
|
||||
}
|
||||
}
|
||||
|
||||
// Mix-in: Hang Punctuation Mark
|
||||
@mixin hang {
|
||||
position: absolute;
|
||||
line-height: inherit;
|
||||
text-indent: 0;
|
||||
}
|
||||
|
||||
@mixin darkmode-style($darkmode: $darkmode, $dark-selector: $manualmode-dark-selector, $auto-selector: $manualmode-auto-selector) {
|
||||
// 'manual' darkmode should provide darkmode selector and auto-darkmode selector
|
||||
@if $darkmode == 'manual' {
|
||||
#{$dark-selector} {
|
||||
@content;
|
||||
}
|
||||
|
||||
@media (prefers-color-scheme: dark) {
|
||||
#{$auto-selector} {
|
||||
@content;
|
||||
}
|
||||
}
|
||||
} @else if $darkmode {
|
||||
@media (prefers-color-scheme: dark) {
|
||||
@content;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 函数 Functions
|
||||
// Function: batch prefix/suffix list-item
|
||||
// batch-fix-list((2, 3), '#{$root-selector}--columns-') => (.heti--columns-2, .heti--columns-3)
|
||||
@function batch-fix-list($list, $prefix: '', $suffix: '') {
|
||||
$_list: () !default;
|
||||
|
||||
@each $item in $list {
|
||||
$_list: append($_list, #{$prefix}#{$item}#{$suffix}, comma);
|
||||
}
|
||||
|
||||
@return $_list;
|
||||
}
|
232
lib/fonts/_hei.scss
Normal file
|
@ -0,0 +1,232 @@
|
|||
// Author: Sivan [sun.sivan@gmail.com], Pan RZ [c141028@gmail.com]
|
||||
// Description: define font-face Heti Hei.
|
||||
|
||||
// 标准 Regular
|
||||
@font-face {
|
||||
font-family: "Heti Hei";
|
||||
src:
|
||||
"Heti Hei SC",
|
||||
"Heti Hei TC",
|
||||
"Heti Hei JP",
|
||||
"Heti Hei KR";
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: "Heti Hei SC";
|
||||
src:
|
||||
local("PingFang SC Regular"),
|
||||
local("Heiti SC Regular"),
|
||||
local("Microsoft YaHei"),
|
||||
local("Source Han Sans CN Regular"),
|
||||
local("Noto Sans CJK SC Regular"),
|
||||
local("WenQuanYi Micro Hei"),
|
||||
local("Droid Sans Fallback");
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: "Heti Hei TC";
|
||||
src:
|
||||
local("PingFang TC Regular"),
|
||||
local("Heiti TC Regular"),
|
||||
local("Microsoft Jhenghei"),
|
||||
local("Source Han Sans HK Regular"),
|
||||
local("Source Han Sans TW Regular"),
|
||||
local("Noto Sans CJK TC Regular"),
|
||||
local("WenQuanYi Micro Hei"),
|
||||
local("Droid Sans Fallback");
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: "Heti Hei JP";
|
||||
src:
|
||||
local("Hiragino Sans GB W3"),
|
||||
local("Source Han Sans JP Regular"),
|
||||
local("Noto Sans CJK JP Regular"),
|
||||
local("Droid Sans Fallback");
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: "Heti Hei KR";
|
||||
src:
|
||||
local("Source Han Sans KR Regular"),
|
||||
local("Noto Sans CJK KR Regular"),
|
||||
local("Droid Sans Fallback");
|
||||
}
|
||||
|
||||
// 细体 Light
|
||||
@font-face {
|
||||
font-family: "Heti Hei";
|
||||
font-weight: 200;
|
||||
src:
|
||||
"Heti Hei SC Light",
|
||||
"Heti Hei TC Light",
|
||||
"Heti Hei JP Light",
|
||||
"Heti Hei KR Light";
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: "Heti Hei SC Light";
|
||||
font-weight: 200;
|
||||
src:
|
||||
local("PingFang SC Light"),
|
||||
local("Heiti SC Light"),
|
||||
"Heti Hei SC Light Fallback",
|
||||
local("Source Han Sans CN Light"),
|
||||
local("Noto Sans CJK SC Light");
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: "Heti Hei TC Light";
|
||||
font-weight: 200;
|
||||
src:
|
||||
local("PingFang TC Light"),
|
||||
local("Heiti TC Light"),
|
||||
local("Microsoft Jhenghei Light"),
|
||||
local("Source Han Sans HK Light"),
|
||||
local("Source Han Sans TW Light"),
|
||||
local("Noto Sans CJK TC Light");
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: "Heti Hei JP Light";
|
||||
font-weight: 200;
|
||||
src:
|
||||
local("Source Han Sans JP Light"),
|
||||
local("Noto Sans CJK JP Light");
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: "Heti Hei KR Light";
|
||||
font-weight: 200;
|
||||
src:
|
||||
local("Source Han Sans KR Light"),
|
||||
local("Noto Sans CJK KR Light");
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: "Heti Hei SC Light Fallback";
|
||||
font-weight: 200;
|
||||
src:
|
||||
local("Microsoft YaHei"),
|
||||
local("Droid Sans Fallback");
|
||||
}
|
||||
|
||||
// 粗体 Bold
|
||||
@font-face {
|
||||
font-family: "Heti Hei";
|
||||
font-weight: 600;
|
||||
src:
|
||||
"Heti Hei SC Bold",
|
||||
"Heti Hei TC Bold",
|
||||
"Heti Hei JP Bold",
|
||||
"Heti Hei KR Bold";
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: "Heti Hei SC Bold";
|
||||
font-weight: 600;
|
||||
src:
|
||||
local("PingFang SC Medium"),
|
||||
local("Heiti SC Medium"),
|
||||
"Heti Hei SC Bold Fallback",
|
||||
local("Source Han Sans CN Bold"),
|
||||
local("Noto Sans CJK SC Bold");
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: "Heti Hei TC Bold";
|
||||
font-weight: 600;
|
||||
src:
|
||||
local("PingFang TC Medium"),
|
||||
local("Heiti TC Medium"),
|
||||
local("Microsoft Jhenghei Bold"),
|
||||
local("Source Han Sans HK Bold"),
|
||||
local("Source Han Sans TW Bold"),
|
||||
local("Noto Sans CJK TC Bold");
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: "Heti Hei JP Bold";
|
||||
font-weight: 600;
|
||||
src:
|
||||
local("Hiragino Sans GB W6"),
|
||||
local("Source Han Sans JP Bold"),
|
||||
local("Noto Sans CJK JP Bold");
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: "Heti Hei KR Bold";
|
||||
font-weight: 600;
|
||||
src:
|
||||
local("Source Han Sans KR Bold"),
|
||||
local("Noto Sans CJK KR Bold");
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: "Heti Hei SC Bold Fallback";
|
||||
font-weight: 600;
|
||||
src:
|
||||
local("Microsoft YaHei"),
|
||||
local("Droid Sans Fallback");
|
||||
}
|
||||
|
||||
// 黑体 Black
|
||||
@font-face {
|
||||
font-family: "Heti Hei Black";
|
||||
font-weight: 800;
|
||||
src:
|
||||
"Heti Hei SC Black",
|
||||
"Heti Hei TC Black",
|
||||
"Heti Hei JP Black",
|
||||
"Heti Hei KR Black";
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: "Heti Hei SC Black";
|
||||
font-weight: 800;
|
||||
src:
|
||||
local("Lantinghei SC Heavy"),
|
||||
local("PingFang SC Semibold"),
|
||||
local("Heiti SC Medium"),
|
||||
"Heti Hei SC Black Fallback",
|
||||
local("Source Han Sans CN Heavy"),
|
||||
local("Noto Sans CJK SC Heavy");
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: "Heti Hei TC Black";
|
||||
font-weight: 800;
|
||||
src:
|
||||
local("Lantinghei TC Heavy"),
|
||||
local("PingFang TC Semibold"),
|
||||
local("Heiti TC Medium"),
|
||||
local("Microsoft Jhenghei Bold"),
|
||||
local("Source Han Sans HK Heavy"),
|
||||
local("Source Han Sans TW Heavy"),
|
||||
local("Noto Sans CJK TC Heavy");
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: "Heti Hei JP Black";
|
||||
font-weight: 800;
|
||||
src:
|
||||
local("Hiragino Sans GB W6"),
|
||||
local("Source Han Sans JP Heavy"),
|
||||
local("Noto Sans CJK JP Heavy");
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: "Heti Hei KR Black";
|
||||
font-weight: 800;
|
||||
src:
|
||||
local("Source Han Sans KR Heavy"),
|
||||
local("Noto Sans CJK KR Heavy");
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: "Heti Hei SC Black Fallback";
|
||||
font-weight: 800;
|
||||
src:
|
||||
local("Microsoft YaHei"),
|
||||
local("Droid Sans Fallback");
|
||||
}
|
42
lib/fonts/_kai.scss
Normal file
|
@ -0,0 +1,42 @@
|
|||
// Author: Sivan [sun.sivan@gmail.com], Pan RZ [c141028@gmail.com]
|
||||
// Description: define font-face Heti Kai.
|
||||
|
||||
// 标准 Regular
|
||||
@font-face {
|
||||
font-family: "Heti Kai";
|
||||
src:
|
||||
local("Kaiti SC Regular"),
|
||||
local("Kaiti TC Regular"),
|
||||
local("STKaiti"),
|
||||
local("Kaiti"),
|
||||
local("BiauKai");
|
||||
}
|
||||
|
||||
// 粗体 Bold
|
||||
@font-face {
|
||||
font-family: "Heti Kai";
|
||||
font-weight: 600;
|
||||
src:
|
||||
local("Kaiti SC Bold"),
|
||||
local("Kaiti TC Bold");
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: "Heti Kai Bold Fallback";
|
||||
font-weight: 600;
|
||||
src:
|
||||
local("STKaiti"),
|
||||
local("Kaiti")
|
||||
local("BiauKai");
|
||||
}
|
||||
|
||||
// 黑体 Black
|
||||
@font-face {
|
||||
font-family: "Heti Kai Black";
|
||||
font-weight: 800;
|
||||
src:
|
||||
local("Kaiti SC Black"),
|
||||
local("Kaiti TC Black"),
|
||||
local("STKaiti"),
|
||||
local("Kaiti");
|
||||
}
|
54
lib/fonts/_song.scss
Normal file
|
@ -0,0 +1,54 @@
|
|||
// Author: Sivan [sun.sivan@gmail.com], Pan RZ [c141028@gmail.com]
|
||||
// Description: define font-face Heti Song.
|
||||
|
||||
// 标准 Regular
|
||||
@font-face {
|
||||
font-family: "Heti Song";
|
||||
src:
|
||||
local("Songti SC Regular"),
|
||||
local("Songti TC Regular"),
|
||||
local("SimSun");
|
||||
}
|
||||
|
||||
// 细体 Light
|
||||
@font-face {
|
||||
font-family: "Heti Song";
|
||||
font-weight: 200;
|
||||
src:
|
||||
local("Songti SC Light"),
|
||||
local("Songti TC Light"),
|
||||
"Heti Song Light Fallback";
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: "Heti Song Light Fallback";
|
||||
font-weight: 200;
|
||||
src: local("SimSun");
|
||||
}
|
||||
|
||||
// 粗体 Bold
|
||||
@font-face {
|
||||
font-family: "Heti Song";
|
||||
font-weight: 600;
|
||||
src:
|
||||
local("Songti SC Bold"),
|
||||
local("Songti TC Bold"),
|
||||
"Heti Song Bold Fallback";
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: "Heti Song Bold Fallback";
|
||||
font-weight: 600;
|
||||
src: local("SimSun");
|
||||
}
|
||||
|
||||
// 黑体 Black
|
||||
// 只支持简体
|
||||
@font-face {
|
||||
font-family: "Heti Song Black";
|
||||
font-weight: 800;
|
||||
src:
|
||||
local("Songti SC Black"),
|
||||
// local("Songti TC Black"),
|
||||
local("SimSun");
|
||||
}
|
35
lib/helpers/_add-on.scss
Normal file
|
@ -0,0 +1,35 @@
|
|||
// Author: Sivan [sun.sivan@gmail.com]
|
||||
// Description: define add-ons.
|
||||
@import "../variables";
|
||||
|
||||
@mixin hetiAddOns {
|
||||
// 用于中西文混排增加边距
|
||||
heti-spacing {
|
||||
display: inline;
|
||||
|
||||
& + sup,
|
||||
& + sub {
|
||||
margin-inline-start: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.heti-spacing-start {
|
||||
margin-inline-end: 0.25em;
|
||||
}
|
||||
|
||||
.heti-spacing-end {
|
||||
margin-inline-start: 0.25em;
|
||||
}
|
||||
|
||||
heti-adjacent {
|
||||
display: inline;
|
||||
}
|
||||
|
||||
.heti-adjacent-half {
|
||||
margin-inline-end: -0.5em;
|
||||
}
|
||||
|
||||
.heti-adjacent-quarter {
|
||||
margin-inline-end: -0.25em;
|
||||
}
|
||||
}
|
98
lib/helpers/_block.scss
Normal file
|
@ -0,0 +1,98 @@
|
|||
// Author: Sivan [sun.sivan@gmail.com]
|
||||
// Description: define block helper classes.
|
||||
@import "../variables";
|
||||
|
||||
@mixin hetiBlockHelperClasses {
|
||||
// 段落相关
|
||||
// 元信息无缩进
|
||||
#{$root-selector}-meta {
|
||||
display: block;
|
||||
text-indent: 0;
|
||||
}
|
||||
|
||||
// 诗节无缩进,居中显示
|
||||
#{$root-selector}-verse {
|
||||
text-align: center;
|
||||
text-indent: 0;
|
||||
}
|
||||
|
||||
// 定义扩展字号
|
||||
#{$root-selector} {
|
||||
&-large {
|
||||
font-size: $font-size-large;
|
||||
line-height: $line-height-size-large;
|
||||
}
|
||||
|
||||
&-x-large {
|
||||
font-size: $font-size-x-large;
|
||||
line-height: $line-height-size-x-large;
|
||||
letter-spacing: 0.05em;
|
||||
}
|
||||
|
||||
&-small {
|
||||
font-size: $font-size-small;
|
||||
line-height: $line-height-size-small;
|
||||
}
|
||||
|
||||
&-x-small {
|
||||
font-size: $font-size-x-small;
|
||||
line-height: $line-height-size-x-small;
|
||||
}
|
||||
}
|
||||
|
||||
// 列表相关
|
||||
// 定义拉丁字母的有序列表
|
||||
#{$root-selector}-list-latin {
|
||||
list-style-type: upper-latin;
|
||||
|
||||
ol {
|
||||
list-style-type: lower-roman;
|
||||
|
||||
ol {
|
||||
list-style-type: lower-latin;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 定义中文序号的有序列表
|
||||
#{$root-selector}-list-han {
|
||||
list-style-type: cjk-ideographic;
|
||||
|
||||
ol {
|
||||
list-style-type: decimal;
|
||||
|
||||
ol {
|
||||
list-style-type: decimal-leading-zero;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 页脚
|
||||
#{$root-selector}-fn {
|
||||
margin-block-start: 59px;
|
||||
border-block-start: 1px solid;
|
||||
border-block-start-color: hsl(0, 0%, 80%);
|
||||
font-size: $font-size-small;
|
||||
font-family: $font-family-hei;
|
||||
line-height: $line-height-size-normal;
|
||||
|
||||
@include darkmode-style {
|
||||
border-block-start-color: hsl(0, 0%, 25%);
|
||||
}
|
||||
|
||||
ol {
|
||||
margin-block-start: $std-block-unit * 0.5;
|
||||
margin-block-end: 0;
|
||||
}
|
||||
|
||||
li {
|
||||
&:target {
|
||||
background-color: hsl(210, 100%, 93%);
|
||||
|
||||
@include darkmode-style {
|
||||
background-color: hsl(210, 40%, 38%);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
43
lib/helpers/_inline.scss
Normal file
|
@ -0,0 +1,43 @@
|
|||
// Author: Sivan [sun.sivan@gmail.com]
|
||||
// Description: define inline helper classes.
|
||||
@import "../variables";
|
||||
|
||||
@mixin hetiInlineHelperClasses {
|
||||
// 标点悬挂 Punctuation mark hanging
|
||||
// @todo: 用于标点悬挂用的样式
|
||||
#{$root-selector}-hang {
|
||||
@include hang();
|
||||
}
|
||||
|
||||
// 强烈着重号 Emphasis Mark
|
||||
#{$root-selector}-em {
|
||||
-webkit-text-emphasis: filled circle;
|
||||
-webkit-text-emphasis-position: under;
|
||||
text-emphasis: filled circle;
|
||||
text-emphasis-position: under right;
|
||||
|
||||
@include non-cjk-block {
|
||||
-webkit-text-emphasis: none;
|
||||
text-emphasis: none;
|
||||
}
|
||||
}
|
||||
|
||||
// 内联 Ruby
|
||||
// 在非行间注排版中使用 ruby 标签,且不额外占据空间
|
||||
// 注:使用此样式需按字拆分 <ruby class="heti-ruby heti-ruby--inline"><rb>赫</rb><rp>(</rp><rt>hè</rt><rp>)</rp></ruby>
|
||||
// https://stackoverflow.com/questions/38680695/adjust-the-vertical-positioning-of-ruby-text/38877801#38877801
|
||||
#{$root-selector}-ruby {
|
||||
&--inline {
|
||||
display: inline-flex;
|
||||
flex-direction: column-reverse;
|
||||
height: 1.5em;
|
||||
|
||||
rt {
|
||||
display: inline;
|
||||
margin-bottom: -0.25em;
|
||||
line-height: 1;
|
||||
text-align: center;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
63
lib/heti.scss
Normal file
|
@ -0,0 +1,63 @@
|
|||
/*!
|
||||
* Project: Heti
|
||||
* URL: https://github.com/sivan/heti
|
||||
* Author: Sivan [sun.sivan@gmail.com]
|
||||
*/
|
||||
@import "font";
|
||||
@import "variables";
|
||||
@import "base";
|
||||
@import "heading";
|
||||
@import "list";
|
||||
@import "table";
|
||||
@import "inline";
|
||||
@import "modifiers/font-stack";
|
||||
@import "modifiers/writing-mode";
|
||||
@import "modifiers/column";
|
||||
@import "modifiers/ancient";
|
||||
@import "modifiers/annotation";
|
||||
@import "helpers/block";
|
||||
@import "helpers/inline";
|
||||
@import "helpers/add-on";
|
||||
|
||||
#{$root-selector} {
|
||||
// 中文每行展示文字(CPL)建议在 30~50 之间,默认 42
|
||||
max-width: $line-length;
|
||||
|
||||
// 默认字体大小为 16px,行高 1.5
|
||||
font-size: $font-size-normal;
|
||||
font-weight: $font-weight-normal;
|
||||
-webkit-font-smoothing: subpixel-antialiased;
|
||||
|
||||
// 针对混合英文段落,采取按词折行,长单词通过连词符段行
|
||||
// https://justmarkup.com/articles/2015-07-31-dealing-with-long-words-in-css/
|
||||
overflow-wrap: break-word;
|
||||
word-wrap: break-word;
|
||||
hyphens: auto;
|
||||
|
||||
// 自动在中西文间加 1/4 空格(暂无浏览器支持)
|
||||
//text-spacing: ideograph-alpha;
|
||||
|
||||
// 模块引用顺序
|
||||
|
||||
// 1. 引入各模块
|
||||
// .heti h1, .heti p, .heti ul
|
||||
@include hetiBase();
|
||||
@include hetiHeading();
|
||||
@include hetiList();
|
||||
@include hetiTable();
|
||||
@include hetiInline();
|
||||
|
||||
// 2. 定义所有修饰器(需与 .heti 组合使用)
|
||||
// .heti--sans h1, .heti--vertical h1 etc.
|
||||
@include hetiFontModifier();
|
||||
@include hetiColumnModifier();
|
||||
@include hetiWritingModeModifier();
|
||||
@include hetiAncientModifier();
|
||||
@include hetiAnnotationModifier();
|
||||
|
||||
// 3. 定义工具类样式(仅在 .heti 内部使用)
|
||||
// .heti .heti-verse, .heti .heti-hang
|
||||
@include hetiBlockHelperClasses();
|
||||
@include hetiInlineHelperClasses();
|
||||
@include hetiAddOns();
|
||||
}
|
58
lib/modifiers/ancient.scss
Normal file
|
@ -0,0 +1,58 @@
|
|||
// Author: Sivan [sun.sivan@gmail.com]
|
||||
// Description: define ancient styles.
|
||||
@import "../variables";
|
||||
|
||||
@mixin hetiAncientModifier {
|
||||
// 定义古文、古诗样式
|
||||
&--ancient,
|
||||
&--poetry {
|
||||
// 古文、古诗用宋体(覆盖全局字体)
|
||||
font-family: $font-family-song;
|
||||
|
||||
// 古文标题用楷体(覆盖全局字体)
|
||||
h1,
|
||||
h2,
|
||||
h3,
|
||||
h4,
|
||||
h5,
|
||||
h6 {
|
||||
font-family: $font-family-kai-black;
|
||||
font-weight: $font-weight-bolder;
|
||||
text-align: center;
|
||||
|
||||
// 标题内元信息仅在桌面端采取悬挂处理,且不占据空间影响文字居中
|
||||
#{$root-selector}-meta {
|
||||
font-weight: $font-weight-normal;
|
||||
|
||||
@media screen and (min-width: 640px) {
|
||||
@include hang();
|
||||
|
||||
display: inline;
|
||||
margin-block-start: 4px;
|
||||
margin-inline-start: 8px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#{$root-selector}-meta {
|
||||
line-height: $line-height-size-normal;
|
||||
text-align: center;
|
||||
text-indent: 0;
|
||||
}
|
||||
}
|
||||
|
||||
&--ancient {
|
||||
// 古文(文言文、词)首行缩进
|
||||
p {
|
||||
text-indent: $text-indent-length;
|
||||
}
|
||||
}
|
||||
|
||||
&--poetry {
|
||||
// 诗节无缩进,居中显示
|
||||
p {
|
||||
text-align: center;
|
||||
text-indent: 0;
|
||||
}
|
||||
}
|
||||
}
|
38
lib/modifiers/annotation.scss
Normal file
|
@ -0,0 +1,38 @@
|
|||
// Author: Sivan [sun.sivan@gmail.com]
|
||||
// Description: define interlinear annotation styles.
|
||||
@import "../variables";
|
||||
|
||||
@mixin hetiAnnotationModifier {
|
||||
&--annotation {
|
||||
// 首行缩进且行间距加大,去除段落间距
|
||||
p {
|
||||
margin-block-start: 0;
|
||||
margin-block-end: 0;
|
||||
line-height: $line-height-expanded-ultra;
|
||||
text-indent: $text-indent-length;
|
||||
}
|
||||
|
||||
// 着重号不应影响行间距,经测试最小可用行高为 1.7
|
||||
em {
|
||||
-webkit-text-emphasis: filled circle;
|
||||
-webkit-text-emphasis-position: under;
|
||||
text-emphasis: filled circle;
|
||||
text-emphasis-position: under right;
|
||||
font-weight: $font-weight-normal;
|
||||
|
||||
@include non-cjk-block {
|
||||
-webkit-text-emphasis: none;
|
||||
text-emphasis: none;
|
||||
}
|
||||
}
|
||||
|
||||
// 元信息保持间距
|
||||
#{$root-selector}-meta {
|
||||
margin-block-start: $std-block-unit * 0.5;
|
||||
margin-block-end: $std-block-unit;
|
||||
}
|
||||
|
||||
// ruby 不应影响行间距,经测试最小可用行高为 2
|
||||
//ruby {}
|
||||
}
|
||||
}
|
33
lib/modifiers/column.scss
Normal file
|
@ -0,0 +1,33 @@
|
|||
// Author: Sivan [sun.sivan@gmail.com]
|
||||
// Description: define column styles.
|
||||
@import "../variables";
|
||||
|
||||
@mixin hetiColumnModifier {
|
||||
// 定义多栏排版
|
||||
$selector-list: batch-fix-list(join($column-count-list, $column-width-list), '&--columns-');
|
||||
|
||||
#{join($selector-list, comma)} {
|
||||
// 多行时不再设总宽度限制
|
||||
max-width: none;
|
||||
column-gap: 2em;
|
||||
|
||||
// 多栏排版时减半段间距
|
||||
p {
|
||||
margin-block-start: $std-block-unit * 0.5 * 0.5;
|
||||
margin-block-end: $std-block-unit * 0.5;
|
||||
text-indent: $text-indent-length;
|
||||
}
|
||||
}
|
||||
|
||||
@each $columns in $column-count-list {
|
||||
&--columns-#{$columns} {
|
||||
column-count: #{$columns};
|
||||
}
|
||||
}
|
||||
|
||||
@each $column-width in $column-width-list {
|
||||
&--columns-#{$column-width} {
|
||||
column-width: #{$column-width};
|
||||
}
|
||||
}
|
||||
}
|
62
lib/modifiers/font-stack.scss
Normal file
|
@ -0,0 +1,62 @@
|
|||
// Author: Sivan [sun.sivan@gmail.com]
|
||||
// Description: define global font stacks.
|
||||
@import "../variables";
|
||||
|
||||
@mixin hetiFontModifier {
|
||||
// 定义默认采用无衬线字体
|
||||
// 定义无衬线字体为黑体
|
||||
&,
|
||||
&--sans {
|
||||
font-family: $font-family-hei;
|
||||
}
|
||||
|
||||
// 定义衬线字体为宋体
|
||||
&--serif {
|
||||
font-family: $font-family-song;
|
||||
}
|
||||
|
||||
// 定义传统字体
|
||||
// 「传统字体」仅供设计参考。主旨为标题用楷体,正文用宋体,表头等客观指标描述场合用黑体。
|
||||
// 由于渲染机制等原因,应尽量避免衬线、非衬线字体混排,会引起行高不一致的bug。
|
||||
&--classic {
|
||||
// 正文使用宋体
|
||||
font-family: $font-family-song;
|
||||
|
||||
// 标题使用楷体 800 字重
|
||||
h1,
|
||||
h2,
|
||||
h3,
|
||||
h4,
|
||||
h5,
|
||||
h6 {
|
||||
font-family: $font-family-kai-black;
|
||||
font-weight: $font-weight-bolder;
|
||||
}
|
||||
|
||||
// 引用使用楷体
|
||||
blockquote,
|
||||
cite,
|
||||
q {
|
||||
font-family: $font-family-kai;
|
||||
}
|
||||
|
||||
// 说明文字、表头等反应客观指标、事物的位置使用黑体
|
||||
figcaption,
|
||||
caption,
|
||||
th {
|
||||
font-family: $font-family-hei;
|
||||
}
|
||||
}
|
||||
|
||||
&--hei {
|
||||
font-family: $font-family-hei;
|
||||
}
|
||||
|
||||
&--song {
|
||||
font-family: $font-family-song;
|
||||
}
|
||||
|
||||
&--kai {
|
||||
font-family: $font-family-kai;
|
||||
}
|
||||
}
|
27
lib/modifiers/writing-mode.scss
Normal file
|
@ -0,0 +1,27 @@
|
|||
// Author: Sivan [sun.sivan@gmail.com]
|
||||
// Description: define writing mode styles.
|
||||
@import "../variables";
|
||||
|
||||
@mixin hetiWritingModeModifier {
|
||||
// 定义垂直布局
|
||||
&--vertical {
|
||||
max-width: none;
|
||||
max-height: $line-length;
|
||||
writing-mode: vertical-rl;
|
||||
letter-spacing: 0.125em;
|
||||
|
||||
h1,
|
||||
h2,
|
||||
h3,
|
||||
h4,
|
||||
h5,
|
||||
h6 {
|
||||
text-align: start;
|
||||
}
|
||||
|
||||
// 默认无论简繁都采用台湾规范,修改只需重新定义变量 `$chinese-quote-set` 即可。
|
||||
q {
|
||||
quotes: map-get(map-get($chinese-quote-presets, $chinese-quote-set), "vertical");
|
||||
}
|
||||
}
|
||||
}
|
6341
package-lock.json
generated
Normal file
80
package.json
Normal file
|
@ -0,0 +1,80 @@
|
|||
{
|
||||
"name": "heti",
|
||||
"version": "0.9.2",
|
||||
"description": "赫蹏是专为中文内容展示设计的排版样式增强。它基于通行的中文排版规范而来,可以为网站的读者带来更好的文章阅读体验。",
|
||||
"main": "lib/heti.scss",
|
||||
"files": [
|
||||
"umd",
|
||||
"js",
|
||||
"lib"
|
||||
],
|
||||
"scripts": {
|
||||
"start": "sass -w --no-source-map _site/scss/:_site/",
|
||||
"compile": "rollup -c -w",
|
||||
"build:style": "sass --no-source-map --style=compressed lib/heti.scss:umd/heti.min.css",
|
||||
"build:site": "sass --no-source-map _site/scss/:_site/",
|
||||
"build:script": "rollup -c",
|
||||
"build": "npm run build:style && npm run build:site && npm run build:script",
|
||||
"test": "npx stylelint --config package.json 'lib/**/*.scss'"
|
||||
},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "git+https://github.com/sivan/heti.git"
|
||||
},
|
||||
"keywords": [
|
||||
"typography",
|
||||
"clreq",
|
||||
"css",
|
||||
"sass",
|
||||
"scss"
|
||||
],
|
||||
"author": "Sivan <sun.sivan@gmail.com>",
|
||||
"license": "MIT",
|
||||
"bugs": {
|
||||
"url": "https://github.com/sivan/heti/issues"
|
||||
},
|
||||
"homepage": "https://github.com/sivan/heti#readme",
|
||||
"dependencies": {
|
||||
"heti-findandreplacedomtext": "^0.5.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@rollup/plugin-commonjs": "^11.1.0",
|
||||
"@rollup/plugin-node-resolve": "^7.1.3",
|
||||
"rollup": "^1.32.1",
|
||||
"rollup-plugin-terser": "^5.3.1",
|
||||
"sass": "^1.57.0",
|
||||
"stylelint": "^13.13.1",
|
||||
"stylelint-config-recommended-scss": "^4.3.0",
|
||||
"stylelint-config-standard": "^22.0.0",
|
||||
"stylelint-scss": "^3.21.0"
|
||||
},
|
||||
"stylelint": {
|
||||
"extends": [
|
||||
"stylelint-config-standard",
|
||||
"stylelint-config-recommended-scss"
|
||||
],
|
||||
"rules": {
|
||||
"no-descending-specificity": null,
|
||||
"at-rule-empty-line-before": null,
|
||||
"block-opening-brace-space-before": "always",
|
||||
"block-closing-brace-newline-after": [
|
||||
"always",
|
||||
{
|
||||
"ignoreAtRules": [
|
||||
"if",
|
||||
"else"
|
||||
]
|
||||
}
|
||||
],
|
||||
"rule-empty-line-before": null,
|
||||
"selector-type-no-unknown": [
|
||||
true,
|
||||
{
|
||||
"ignore": [
|
||||
"custom-elements"
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
33
rollup.config.js
Normal file
|
@ -0,0 +1,33 @@
|
|||
import commonjs from '@rollup/plugin-commonjs';
|
||||
import resolve from '@rollup/plugin-node-resolve';
|
||||
import {terser} from 'rollup-plugin-terser';
|
||||
|
||||
export default {
|
||||
input: 'js/heti-addon.js',
|
||||
output: [
|
||||
{
|
||||
file: '_site/heti-addon.js',
|
||||
name: 'Heti',
|
||||
format: 'umd'
|
||||
},
|
||||
{
|
||||
file: 'umd/heti-addon.min.js',
|
||||
format: 'umd',
|
||||
name: 'Heti',
|
||||
plugins: [
|
||||
terser({
|
||||
compress: {
|
||||
pure_funcs: ["console.info"] // 移除调试信息
|
||||
},
|
||||
output: {
|
||||
comments: false
|
||||
}
|
||||
})
|
||||
]
|
||||
},
|
||||
],
|
||||
plugins: [
|
||||
resolve(),
|
||||
commonjs(),
|
||||
]
|
||||
};
|