New preact based frontend UI for the TTT.

Added rollup compiling
This commit is contained in:
Jocelyn Badgley (Twipped) 2020-03-08 14:01:00 -07:00
parent 67b168dba1
commit dad24e1199
33 changed files with 2322 additions and 172 deletions

View file

@ -1,3 +1,3 @@
last 2 major version
>0.5%
not dead
not ie < 12

12
babel.config.js Normal file
View file

@ -0,0 +1,12 @@
module.exports = exports = {
plugins: [
[ '@babel/plugin-proposal-class-properties', { loose: true } ],
],
presets: [
[ '@babel/preset-env', {
// useBuiltIns: 'usage',
} ],
'preact',
],
};

View file

@ -20,7 +20,7 @@ module.exports = exports = class File {
const file = path.parse(filepath);
this._basename();
this._basename(file);
this.kind = kind(filepath);
this.type = type(filepath);
@ -28,7 +28,7 @@ module.exports = exports = class File {
this.cwd = file.dir;
this.ext = this.preprocessed ? file.ext : normalizedExt(file.ext);
this.name = file.name; // index, fileA, fileB
this.basename = file.basename; // index.ext, fileA.ext, fileB.ext
this.basename = file.base; // index.ext, fileA.ext, fileB.ext
const dir = this._dir(file.dir);
if (dir) {
@ -59,7 +59,7 @@ module.exports = exports = class File {
if (file.name[0] === '_') {
this.preprocessed = true;
file.name = file.name.slice(1);
file.basename = file.basename.slice(1);
file.base = file.base.slice(1);
}
}

View file

@ -8,6 +8,7 @@ const { siteInfo } = require(resolve('package.json'));
module.exports = exports = async function writePageContent (engines, pages, posts, prod) {
const postIndex = index(posts, engines);
await processPages(engines, [ ...posts, ...pages ], postIndex, prod);
postIndex.latest = { ...pageJSON(postIndex.latest), content: postIndex.latest.content };
return postIndex;
};
@ -78,6 +79,7 @@ function pageState (page, posts) {
function pageJSON (post) {
return {
id: post.id,
url: post.url,
fullurl: post.fullurl,
json: '/' + post.json,

View file

@ -72,6 +72,8 @@ module.exports = exports = class Post extends Page {
_parse (...args) {
super._parse(...args);
this.id = this.meta.id;
if (!this.titlecard) this.titlecard = '/tweets/titlecard.png';
this.meta.tags = (this.meta.tags || []).reduce((result, tag) => {

View file

@ -37,6 +37,7 @@ const EXT = exports.EXT = {
CSS: '.css',
SCSS: '.scss',
JS: '.js',
JSX: '.jsx',
};
const {
@ -53,12 +54,14 @@ const {
CSS,
SCSS,
JS,
JSX,
} = EXT;
const NORMALIZE_EXT = {
[JPG]: JPEG,
[M4V]: MP4,
[HBS]: HTML,
[JSX]: JS,
};
const normalizedExt = exports.normalizedExt = (ext) => {
@ -72,7 +75,7 @@ const isHandlebars = exports.isHandlebars = is(XML, HBS, HTML);
const isMarkdown = exports.isMarkdown = is(MD);
const isPage = exports.isPage = is(isHandlebars, isMarkdown);
const isAsset = exports.isAsset = is(isImage, isVideo);
const isArtifact = exports.isArtifact = is(CSS, SCSS, JS);
const isArtifact = exports.isArtifact = is(CSS, SCSS, JS, JSX);
exports.isCleanUrl = is(HBS, MD);
@ -92,7 +95,7 @@ exports.type = dictMatch({
[TYPE.HANDLEBARS]: isHandlebars,
[TYPE.MARKDOWN]: isMarkdown,
[TYPE.VIDEO]: isVideo,
[TYPE.SCRIPT]: is(JS),
[TYPE.SCRIPT]: is(JS, JSX),
[TYPE.STYLE]: is(SCSS, CSS),
}, TYPE.OTHER);

53
build/rollup.js Normal file
View file

@ -0,0 +1,53 @@
const { resolve } = require('./resolve');
const { rollup } = require('rollup');
const alias = require('@rollup/plugin-alias');
const commonjs = require('@rollup/plugin-commonjs');
const nodeResolve = require('@rollup/plugin-node-resolve');
const replace = require('@rollup/plugin-replace');
const babel = require('rollup-plugin-babel');
const svg = require('rollup-plugin-react-svg');
const { terser } = require('rollup-plugin-terser');
const plugins = [
replace({ 'process.env.NODE_ENV': '"production"' }),
alias({
entries: [
{ find: 'react', replacement: 'preact/compat' },
{ find: 'react-dom', replacement: 'preact/compat' },
{ find: 'svg', replacement: resolve('svg') },
{ find: 'utils', replacement: resolve('build/lib/util.js') },
],
}),
svg(),
babel({
exclude: 'node_modules/**',
}),
nodeResolve(),
commonjs({
include: 'node_modules/**',
}),
];
module.exports = exports = async function (input, prod) {
const inputOptions = {
input,
plugins,
};
const outputOptions = {
format: 'iife',
sourcemap: 'inline',
plugins: prod
? [ terser({ output: { comments: false } }) ]
: undefined,
};
const bundle = await rollup(inputOptions);
const output = await bundle.generate(outputOptions);
// console.log(output);
return output.output[0].code;
};

View file

@ -1,9 +1,10 @@
const glob = require('./lib/glob');
const { ROOT, readFile } = require('./resolve');
const { ROOT, readFile, resolve } = require('./resolve');
const actions = require('./actions');
const File = require('./file');
const Promise = require('bluebird');
const { minify } = require('terser');
const rollup = require('./rollup');
module.exports = exports = async function scripts (prod) {
const globalFiles = await glob('js/_*.js', { cwd: ROOT, nodir: true });
@ -18,10 +19,12 @@ module.exports = exports = async function scripts (prod) {
const globalScript = new ClientScript('js/global.js');
await globalScript.concat(globalFiles, prod);
const files = await Promise.map(glob('js/*.js', { cwd: ROOT, nodir: true }), async (filepath) => {
const files = await Promise.map(glob('js/*.{js,jsx}', { cwd: ROOT, nodir: true }), async (filepath) => {
const f = new ClientScript(filepath);
if (f.preprocessed) return false;
if (f.globalScript) return false;
await f.load(prod);
return f;
}).filter(Boolean);
@ -35,12 +38,35 @@ module.exports = exports = async function scripts (prod) {
class ClientScript extends File {
_basename (file) {
super._basename(file);
this.globalScript = false;
if (file.name[0] === '_') {
this.globalScript = true;
file.name = file.name.slice(1);
file.base = file.base.slice(1);
}
this.rollup = false;
if (file.name[0] === '$') {
this.rollup = true;
file.name = file.name.slice(1);
file.base = file.base.slice(1);
}
}
_dir (dir) {
dir = dir.split('/');
return dir;
}
async load (prod) {
if (this.rollup) {
this.content = await rollup(resolve(this.input), prod);
return;
}
let contents = (await readFile(this.input).catch(() => '')).toString('utf8');
if (prod) {
const { code, error } = minify(contents);

View file

@ -51,7 +51,7 @@ function watcher () {
], scss);
watch([
'js/*.js',
'js/*.{js,jsx}',
], scripts);
watch([

132
js/$tweets.jsx Normal file
View file

@ -0,0 +1,132 @@
/** @jsx h */
import { h, render, Component, Fragment } from 'preact';
import map from 'lodash/map';
// import memoize from 'lodash/memoize';
import { format } from 'date-fns';
import Sync from 'svg/sync-alt.svg';
import Link from 'svg/link.svg';
// const If = ({t,children}) => (!!t && <Fragment>{children}</Fragment>)
const Raw = ({ html }) => <div dangerouslySetInnerHTML={{ __html: html }} />;
const Post = ({ post }) => (
<article>
<div class="post-head">
<div class="post-tags">
{map(post.tags, (v, k) => <a href={'/tweets/#tag=' + k} class="post-link tag">{v}</a>)}
{map(post.author, (v) => <a href={'/tweets/#author=' + v} class="post-link author">{v}</a>)}
</div>
<a href={post.url} class="post-link" title={format(new Date(post.date), 'MMMM do, yyyy')}><span class="svg-icon"><Link /></span> Permalink</a>
</div>
<div class="post-content">{post.content ? <Raw html={post.content} /> : <div class="loading"><Sync /></div>}</div>
</article>
);
class App extends Component {
constructor (props) {
super(props);
const index = this.props.index;
const hash = window.location.hash.slice(1);
this.state = {
hash,
loading: false,
posts: Object.fromEntries(index.posts.map((post) => [ post.id, post ])),
tags: index.tags,
authors: index.authors,
latest: index.latest,
};
this.loading = new Map();
this.onChange = this.onChange.bind(this);
this.hashedPosts = this.hashedPosts.bind(this);
this.ensurePost = this.ensurePost.bind(this);
}
componentDidMount () {
window.addEventListener('hashchange', this.onChange);
}
componentWillUnmount () {
window.removeEventListener('hashchange', this.onChange);
}
onChange () {
this.setState({
hash: window.location.hash.slice(1),
prevHash: this.state.hash,
});
}
parseHash () {
return this.state.hash && String(this.state.hash).split('=').filter(Boolean) || [];
}
hashedPosts (target, value) {
const posts = Object.values(this.state.posts);
if (!target && !value) return [ this.state.latest ];
return posts.filter((post) => {
// console.log({ post, target, value })
if (target === 'tag') return !!post.tags[value];
if (target === 'author') return post.author.includes(value);
return false;
});
}
ensurePost ({ id, json }) {
if (this.loading.has(id)) return this.loading.get(id);
const p = fetch(json)
.then((res) => res.json())
.then((post) => {
this.setState({
posts: { ...this.state.posts, [post.id]: post },
});
})
.catch(console.error); // eslint-disable-line no-console
this.loading.set(id, p);
return p;
}
render () {
const [ target, value ] = this.parseHash();
const posts = this.hashedPosts(target, value);
if (this.state.loading) {
return <div class="loading"><Sync /></div>;
}
posts.forEach(this.ensurePost);
let caption = null;
if (target === 'tag') caption = <h4>Threads about {this.state.tags[value] || value}</h4>;
if (target === 'author') caption = <h4>Tweets by {value}</h4>;
return (
<Fragment>
{caption}
{map(posts, (post, i) =>
<Post post={post} key={i} />,
)}
</Fragment>
);
}
}
async function run () {
const index = await fetch('/tweets/index.json').then((res) => res.json());
const target = document.querySelector('.post-index section');
while (target.firstChild) {
target.removeChild(target.firstChild);
}
render(<App index={index} />, target);
}
run().catch(console.error); // eslint-disable-line

View file

@ -1,12 +0,0 @@
{
"extends": "twipped/browser",
"env": {"es6": true, "jquery": true},
"rules": {
'indent': [ 2, 2, {
'MemberExpression': 1,
} ],
'prefer-arrow-callback': 0,
'object-shorthand': 0,
'node/no-unsupported-features/node-builtins': 0
}
}

27
js/.eslintrc.js Normal file
View file

@ -0,0 +1,27 @@
module.exports = exports = {
extends: "twipped/browser",
env: {es6: true, jquery: true},
rules: {
'indent': [ 2, 2, {
'MemberExpression': 1,
} ],
'prefer-arrow-callback': 0,
'object-shorthand': 0,
'node/no-unsupported-features/node-builtins': 0
},
overrides: [
{
files: '$*.jsx',
extends: "twipped/react",
rules: {
'react/jsx-indent': [2, 2, {checkAttributes: true}],
"react/no-unknown-property": [2, { ignore: ['class'] }],
'node/no-unpublished-import': 0,
"node/no-missing-import": ["error", {
"allowModules": ["svg", 'react']
}]
}
}
]
};

1817
package-lock.json generated

File diff suppressed because it is too large Load diff

View file

@ -25,8 +25,17 @@
},
"license": "MIT",
"devDependencies": {
"@babel/core": "~7.8.7",
"@babel/plugin-proposal-class-properties": "~7.8.3",
"@babel/preset-env": "~7.8.7",
"@rollup/plugin-alias": "~3.0.1",
"@rollup/plugin-commonjs": "~11.0.2",
"@rollup/plugin-node-resolve": "~7.1.1",
"@rollup/plugin-replace": "~2.3.1",
"autoprefixer": "~9.7.4",
"aws-sdk": "~2.624.0",
"babel-eslint": "~10.1.0",
"babel-preset-preact": "~2.0.0",
"backbone": "~1.4.0",
"bluebird": "~3.7.2",
"bootstrap": "~4.4.1",
@ -37,6 +46,7 @@
"eslint-config-twipped": "~3.4.0",
"eslint-plugin-node": "~11.0.0",
"eslint-plugin-promise": "~4.2.1",
"eslint-plugin-react": "~7.19.0",
"express": "~4.17.1",
"fancy-log": "~1.3.3",
"forever": "~2.0.0",
@ -67,14 +77,13 @@
"png-to-ico": "~2.0.6",
"popper.js": "~1.16.0",
"postcss": "~7.0.27",
"preact": "~10.3.3",
"rev-hash": "~3.0.0",
"rev-path": "~2.0.0",
"rollup": "~1.31.1",
"rollup-plugin-alias": "~2.2.0",
"rollup-plugin-commonjs": "~10.1.0",
"rollup-plugin-json": "~4.0.0",
"rollup-plugin-node-resolve": "~5.2.0",
"rollup-plugin-string": "~3.0.0",
"rollup": "~2.0.2",
"rollup-plugin-babel": "~4.4.0",
"rollup-plugin-react-svg": "~3.0.3",
"rollup-plugin-terser": "~5.2.0",
"rss": "~1.2.2",
"serve-index": "~1.9.1",
"slugify": "~1.3.6",

View file

@ -5,7 +5,8 @@ title: Tweets by @Emmy_Zje
description: 'On the topic of shame and guilt for being trans.'
author: Emmy_Zje
tags:
- Transgender
- Shame
- Euphoria
tweets:
- https://twitter.com/Emmy_Zje/status/1200891565478236161
- https://twitter.com/Emmy_Zje/status/1201138482569195526

View file

@ -1,43 +1,44 @@
<div class="pager {{className}}">
<div class="back">
{{#is meta.url '/gdb/what-is-gender' }}<a href="/gdb/" class="btn btn-primary left">{{icon 'chevron-left'}} Introduction</a>{{/is}}
{{#is meta.url '/gdb/history' }}<a href="/gdb/what-is-gender" class="btn btn-primary left">{{icon 'chevron-left'}} What Is Gender?</a>{{/is}}
{{#is meta.url '/gdb/euphoria' }}<a href="/gdb/history" class="btn btn-primary left">{{icon 'chevron-left'}} The History of Gender Dysphoria</a>{{/is}}
{{#is meta.url '/gdb/physical-dysphoria' }}<a href="/gdb/euphoria" class="btn btn-primary left">{{icon 'chevron-left'}} Gender Euphoria</a>{{/is}}
{{#is meta.url '/gdb/biochemical-dysphoria' }}<a href="/gdb/physical-dysphoria" class="btn btn-primary left">{{icon 'chevron-left'}} Physical Dysphoria</a>{{/is}}
{{#is meta.url '/gdb/social-dysphoria' }}<a href="/gdb/biochemical-dysphoria" class="btn btn-primary left">{{icon 'chevron-left'}} Biochemical Dysphoria</a>{{/is}}
{{#is meta.url '/gdb/societal-dysphoria' }}<a href="/gdb/social-dysphoria" class="btn btn-primary left">{{icon 'chevron-left'}} Social Dysphoria</a>{{/is}}
{{#is meta.url '/gdb/sexual-dysphoria' }}<a href="/gdb/societal-dysphoria" class="btn btn-primary left">{{icon 'chevron-left'}} Societal Dysphoria</a>{{/is}}
{{#is meta.url '/gdb/presentational-dysphoria'}}<a href="/gdb/sexual-dysphoria" class="btn btn-primary left">{{icon 'chevron-left'}} Sexual Dysphoria</a>{{/is}}
{{#is meta.url '/gdb/historical-dysphoria' }}<a href="/gdb/presentational-dysphoria" class="btn btn-primary left">{{icon 'chevron-left'}} Presentational Dysphoria</a>{{/is}}
{{#is meta.url '/gdb/managed-dysphoria' }}<a href="/gdb/historical-dysphoria" class="btn btn-primary left">{{icon 'chevron-left'}} Historical Dysphoria</a>{{/is}}
{{#is meta.url '/gdb/impostor-syndrome' }}<a href="/gdb/managed-dysphoria" class="btn btn-primary left">{{icon 'chevron-left'}} Managed Dysphoria</a>{{/is}}
{{#is meta.url '/gdb/diagnoses' }}<a href="/gdb/impostor-syndrome" class="btn btn-primary left">{{icon 'chevron-left'}} Impostor Syndrome</a>{{/is}}
{{#is meta.url '/gdb/treatment' }}<a href="/gdb/diagnoses" class="btn btn-primary left">{{icon 'chevron-left'}} Clinical Diagnoses</a>{{/is}}
{{#is meta.url '/gdb/causes' }}<a href="/gdb/treatment" class="btn btn-primary left">{{icon 'chevron-left'}} Treating Gender Dysphoria</a>{{/is}}
{{#is meta.url '/gdb/chromosomes' }}<a href="/gdb/causes" class="btn btn-primary left">{{icon 'chevron-left'}} Causes of Gender Dysphoria</a>{{/is}}
{{#is meta.url '/gdb/conclusion' }}<a href="/gdb/chromosomes" class="btn btn-primary left">{{icon 'chevron-left'}} Disorders of Sexual Development</a>{{/is}}
<div class="prev">
{{#is meta.url '/gdb/what-is-gender' }}<a href="/gdb/" class="btn btn-primary left">{{icon 'angle-left'}} Introduction</a>{{/is}}
{{#is meta.url '/gdb/history' }}<a href="/gdb/what-is-gender" class="btn btn-primary left">{{icon 'angle-left'}} What Is Gender?</a>{{/is}}
{{#is meta.url '/gdb/euphoria' }}<a href="/gdb/history" class="btn btn-primary left">{{icon 'angle-left'}} The History of Gender Dysphoria</a>{{/is}}
{{#is meta.url '/gdb/physical-dysphoria' }}<a href="/gdb/euphoria" class="btn btn-primary left">{{icon 'angle-left'}} Gender Euphoria</a>{{/is}}
{{#is meta.url '/gdb/biochemical-dysphoria' }}<a href="/gdb/physical-dysphoria" class="btn btn-primary left">{{icon 'angle-left'}} Physical Dysphoria</a>{{/is}}
{{#is meta.url '/gdb/social-dysphoria' }}<a href="/gdb/biochemical-dysphoria" class="btn btn-primary left">{{icon 'angle-left'}} Biochemical Dysphoria</a>{{/is}}
{{#is meta.url '/gdb/societal-dysphoria' }}<a href="/gdb/social-dysphoria" class="btn btn-primary left">{{icon 'angle-left'}} Social Dysphoria</a>{{/is}}
{{#is meta.url '/gdb/sexual-dysphoria' }}<a href="/gdb/societal-dysphoria" class="btn btn-primary left">{{icon 'angle-left'}} Societal Dysphoria</a>{{/is}}
{{#is meta.url '/gdb/presentational-dysphoria'}}<a href="/gdb/sexual-dysphoria" class="btn btn-primary left">{{icon 'angle-left'}} Sexual Dysphoria</a>{{/is}}
{{#is meta.url '/gdb/historical-dysphoria' }}<a href="/gdb/presentational-dysphoria" class="btn btn-primary left">{{icon 'angle-left'}} Presentational Dysphoria</a>{{/is}}
{{#is meta.url '/gdb/managed-dysphoria' }}<a href="/gdb/historical-dysphoria" class="btn btn-primary left">{{icon 'angle-left'}} Historical Dysphoria</a>{{/is}}
{{#is meta.url '/gdb/impostor-syndrome' }}<a href="/gdb/managed-dysphoria" class="btn btn-primary left">{{icon 'angle-left'}} Managed Dysphoria</a>{{/is}}
{{#is meta.url '/gdb/diagnoses' }}<a href="/gdb/impostor-syndrome" class="btn btn-primary left">{{icon 'angle-left'}} Impostor Syndrome</a>{{/is}}
{{#is meta.url '/gdb/treatment' }}<a href="/gdb/diagnoses" class="btn btn-primary left">{{icon 'angle-left'}} Clinical Diagnoses</a>{{/is}}
{{#is meta.url '/gdb/causes' }}<a href="/gdb/treatment" class="btn btn-primary left">{{icon 'angle-left'}} Treating Gender Dysphoria</a>{{/is}}
{{#is meta.url '/gdb/chromosomes' }}<a href="/gdb/causes" class="btn btn-primary left">{{icon 'angle-left'}} Causes of Gender Dysphoria</a>{{/is}}
{{#is meta.url '/gdb/conclusion' }}<a href="/gdb/chromosomes" class="btn btn-primary left">{{icon 'angle-left'}} Disorders of Sexual Development</a>{{/is}}
</div>
<div class="forward">
{{#is meta.url '/' }}<a href="/gdb/what-is-gender" class="btn btn-primary right">Continue Reading {{icon 'chevron-right'}}</a>{{/is}}
{{#is meta.url '/gdb' }}<a href="/gdb/what-is-gender" class="btn btn-primary right">What is Gender? {{icon 'chevron-right'}}</a>{{/is}}
{{#is meta.url '/gdb/what-is-gender' }}<a href="/gdb/history" class="btn btn-primary right">The History of Gender Dysphoria {{icon 'chevron-right'}}</a>{{/is}}
{{#is meta.url '/gdb/history' }}<a href="/gdb/euphoria" class="btn btn-primary right">Gender Euphoria {{icon 'chevron-right'}}</a>{{/is}}
{{#is meta.url '/gdb/euphoria' }}<a href="/gdb/physical-dysphoria" class="btn btn-primary right">Physical Dysphoria {{icon 'chevron-right'}}</a>{{/is}}
{{#is meta.url '/gdb/physical-dysphoria' }}<a href="/gdb/biochemical-dysphoria" class="btn btn-primary right">Biochemical Dysphoria {{icon 'chevron-right'}}</a>{{/is}}
{{#is meta.url '/gdb/biochemical-dysphoria' }}<a href="/gdb/social-dysphoria" class="btn btn-primary right">Social Dysphoria {{icon 'chevron-right'}}</a>{{/is}}
{{#is meta.url '/gdb/social-dysphoria' }}<a href="/gdb/societal-dysphoria" class="btn btn-primary right">Societal Dysphoria {{icon 'chevron-right'}}</a>{{/is}}
{{#is meta.url '/gdb/societal-dysphoria' }}<a href="/gdb/sexual-dysphoria" class="btn btn-primary right">Sexual Dysphoria {{icon 'chevron-right'}}</a>{{/is}}
{{#is meta.url '/gdb/sexual-dysphoria' }}<a href="/gdb/presentational-dysphoria" class="btn btn-primary right">Presentational Dysphoria {{icon 'chevron-right'}}</a>{{/is}}
{{#is meta.url '/gdb/presentational-dysphoria'}}<a href="/gdb/historical-dysphoria" class="btn btn-primary right">Historical Dysphoria {{icon 'chevron-right'}}</a>{{/is}}
{{#is meta.url '/gdb/historical-dysphoria' }}<a href="/gdb/managed-dysphoria" class="btn btn-primary right">Managed Dysphoria {{icon 'chevron-right'}}</a>{{/is}}
{{#is meta.url '/gdb/managed-dysphoria' }}<a href="/gdb/impostor-syndrome" class="btn btn-primary right">Impostor Syndrome {{icon 'chevron-right'}}</a>{{/is}}
{{#is meta.url '/gdb/impostor-syndrome' }}<a href="/gdb/diagnoses" class="btn btn-primary right">Clinical Diagnoses {{icon 'chevron-right'}}</a>{{/is}}
{{#is meta.url '/gdb/diagnoses' }}<a href="/gdb/treatment" class="btn btn-primary right">Treating Gender Dysphoria {{icon 'chevron-right'}}</a>{{/is}}
{{#is meta.url '/gdb/treatment' }}<a href="/gdb/causes" class="btn btn-primary right">Causes of Gender Dysphoria {{icon 'chevron-right'}}</a>{{/is}}
{{#is meta.url '/gdb/causes' }}<a href="/gdb/chromosomes" class="btn btn-primary right">But... but... the chromosomes! {{icon 'chevron-right'}}</a>{{/is}}
{{#is meta.url '/gdb/chromosomes' }}<a href="/gdb/conclusion" class="btn btn-primary right">Conclusion {{icon 'chevron-right'}}</a>{{/is}}
{{!-- <div class="first"></div>
<div class="last"></div> --}}
<div class="next">
{{#is meta.url '/' }}<a href="/gdb/what-is-gender" class="btn btn-primary right">Continue Reading {{icon 'angle-right'}}</a>{{/is}}
{{#is meta.url '/gdb' }}<a href="/gdb/what-is-gender" class="btn btn-primary right">What is Gender? {{icon 'angle-right'}}</a>{{/is}}
{{#is meta.url '/gdb/what-is-gender' }}<a href="/gdb/history" class="btn btn-primary right">The History of Gender Dysphoria {{icon 'angle-right'}}</a>{{/is}}
{{#is meta.url '/gdb/history' }}<a href="/gdb/euphoria" class="btn btn-primary right">Gender Euphoria {{icon 'angle-right'}}</a>{{/is}}
{{#is meta.url '/gdb/euphoria' }}<a href="/gdb/physical-dysphoria" class="btn btn-primary right">Physical Dysphoria {{icon 'angle-right'}}</a>{{/is}}
{{#is meta.url '/gdb/physical-dysphoria' }}<a href="/gdb/biochemical-dysphoria" class="btn btn-primary right">Biochemical Dysphoria {{icon 'angle-right'}}</a>{{/is}}
{{#is meta.url '/gdb/biochemical-dysphoria' }}<a href="/gdb/social-dysphoria" class="btn btn-primary right">Social Dysphoria {{icon 'angle-right'}}</a>{{/is}}
{{#is meta.url '/gdb/social-dysphoria' }}<a href="/gdb/societal-dysphoria" class="btn btn-primary right">Societal Dysphoria {{icon 'angle-right'}}</a>{{/is}}
{{#is meta.url '/gdb/societal-dysphoria' }}<a href="/gdb/sexual-dysphoria" class="btn btn-primary right">Sexual Dysphoria {{icon 'angle-right'}}</a>{{/is}}
{{#is meta.url '/gdb/sexual-dysphoria' }}<a href="/gdb/presentational-dysphoria" class="btn btn-primary right">Presentational Dysphoria {{icon 'angle-right'}}</a>{{/is}}
{{#is meta.url '/gdb/presentational-dysphoria'}}<a href="/gdb/historical-dysphoria" class="btn btn-primary right">Historical Dysphoria {{icon 'angle-right'}}</a>{{/is}}
{{#is meta.url '/gdb/historical-dysphoria' }}<a href="/gdb/managed-dysphoria" class="btn btn-primary right">Managed Dysphoria {{icon 'angle-right'}}</a>{{/is}}
{{#is meta.url '/gdb/managed-dysphoria' }}<a href="/gdb/impostor-syndrome" class="btn btn-primary right">Impostor Syndrome {{icon 'angle-right'}}</a>{{/is}}
{{#is meta.url '/gdb/impostor-syndrome' }}<a href="/gdb/diagnoses" class="btn btn-primary right">Clinical Diagnoses {{icon 'angle-right'}}</a>{{/is}}
{{#is meta.url '/gdb/diagnoses' }}<a href="/gdb/treatment" class="btn btn-primary right">Treating Gender Dysphoria {{icon 'angle-right'}}</a>{{/is}}
{{#is meta.url '/gdb/treatment' }}<a href="/gdb/causes" class="btn btn-primary right">Causes of Gender Dysphoria {{icon 'angle-right'}}</a>{{/is}}
{{#is meta.url '/gdb/causes' }}<a href="/gdb/chromosomes" class="btn btn-primary right">But... but... the chromosomes! {{icon 'angle-right'}}</a>{{/is}}
{{#is meta.url '/gdb/chromosomes' }}<a href="/gdb/conclusion" class="btn btn-primary right">Conclusion {{icon 'angle-right'}}</a>{{/is}}
</div>
</div>

View file

@ -18,30 +18,54 @@ description: "A collection of the best twitter threads on transgender and gender
<link rel="canonical" href="{{#if canonical}}{{canonical}}{{else}}https://{{page.domain}}{{url}}{{/if}}">
{{/content}}
{{#content "bodyClass"}}page ttt{{/content}}
{{#content "bodyClass"}}post{{/content}}
{{#content "post-header"}}
<div class="disclaimer"><div class="container" style="text-align: center">
<h1>Trans Twitter Topics</h1>
<strong>A collection of the best Twitter threads on transgender and gender dysphoria topics.</strong>
<p>Have a thread that you think belongs here? <a href="https://twitter.com/TwippingVanilla">Send it my way</a>.</p>
</div></div>
{{/content}}
{{#append "postscripts"}}
<script type="text/javascript" src="{{rev '/js/tweets.js'}}"></script>
{{/append}}
{{#content "body"}}
<!-- <h1>Trans Twitter Topics</h1> -->
<div class="disclaimer"><div class="container" style="text-align: center">
<h1>Trans Twitter Topics</h1>
<strong>A collection of the best Twitter threads on transgender and gender dysphoria topics.</strong>
<p>Have a thread that you think belongs here? <a href="https://twitter.com/TwippingVanilla">Send it my way</a>.</p>
</div></div>
<div class="post-index">
<section>
{{#with posts.latest}}
<article>
<div class="post-head">
<div class="post-tags">
{{#each meta.tags}}<a href="#tag={{@key}}" class="post-link tag">{{this}}</a>{{/each}}
{{#each meta.author}}<a href="#author={{this}}" class="post-link author">{{this}}</a>{{/each}}
</div>
<a href="{{this.url}}" class="post-link" title="{{date this.date 'MMMM do, yyyy'}}">{{icon 'link'}} Permalink</a>
</div>
<div class="post-content">{{{this.content}}}</div>
<div class="pager">
<div class="prev">{{#if siblings.prev}}<a href="{{siblings.prev}}" class="btn btn-primary left">{{icon 'angle-left'}} Back</a>{{/if}}</div>
<div class="first">{{#if siblings.first}}<a href="{{siblings.first}}" class="btn btn-primary left">{{icon 'angle-double-left'}} Newest</a>{{/if}}</div>
<div class="last">{{#if siblings.last}}<a href="{{siblings.last}}" class="btn btn-primary right">Oldest {{icon 'angle-double-right'}}</a>{{/if}}</div>
<div class="next">{{#if siblings.next}}<a href="{{siblings.next}}" class="btn btn-primary right">Next {{icon 'angle-right'}}</a>{{/if}}</div>
</div>
</article>
{{/with}}
</section>
<section class="ttt-grid">
{{#each posts}}{{#unless draft}}
<div class="ttt-post {{#if flags.bordered}} bordered{{/if}}">
<div class="ttt-head">
<div class="ttt-tags">{{#each meta.tags}}<a href="#{{@key}}" class="ttt-tag">{{this}}</a>{{/each}}</div>
<a href="{{this.url}}" class="ttt-link" title="{{date this.dateCreated 'MMMM do, yyyy'}}">{{icon 'link'}} Permalink</a>
</div>
<div class="ttt-wrap">{{{this.content}}}</div>
<aside class="tags">
<h4>Topics</h4>
<div class="post-tags">{{#each posts.tags}}<a href="#tag={{@key}}" class="post-link tag">{{this}}</a>{{/each}}</div>
</aside>
<aside class="authors">
<h4>Authors</h4>
<div class="post-tags">{{#each posts.authors}}<a href="#author={{this}}" class="post-link author">{{this}}</a>{{/each}}</div>
</aside>
</div>
{{/unless}}{{/each}}
</section>
{{/content}}
{{/extend}}

View file

@ -1,5 +1,5 @@
body.page {
body.page, body.post {
@media (max-width: 500px) {
font-size: 0.9em;
}
@ -123,33 +123,49 @@ body.gdb {
}
}
body.ttt {
body.post {
$borderColor: #e1e8ed;
$borderHover: #ccd6dd;
$textLight: #697882;
$textDark: #1c2022;
$textHover: #3b94d9;
$borderRadius: .35em;
$articleWidth: 660px;
$sidebarWidth: 220px;
.ttt-grid {
padding: 1em;
max-width: 660px;
margin: 0 auto;
.disclaimer {
margin-bottom: 1em;
}
.ttt-post {
.loading {
min-height: 400px;
display: flex;
align-items: center;
justify-content: center;
// background: red;
color: $gray-400;
svg {
width: 80px;
height: 80px;
animation: spinner-border 1s linear infinite;
}
}
article {
max-width: $articleWidth;
margin: 0 auto 1em;
background: $gutter-bg;
padding: 0.6em 1em;
margin: 0 0 2em 0;
border-radius: $borderRadius;
box-shadow: inset 0 1px 2px rgba($gray-600, 0.2);
.ttt-head {
.post-head {
display: flex;
margin-bottom: 0.5em;
.ttt-tags {
.post-tags {
flex: 1;
display: flex;
flex-wrap: wrap;
@ -163,31 +179,118 @@ body.ttt {
box-shadow: 0 1px 2px rgba($gray-600, 0.5);
border: none;
}
.tweet:first-child {
margin-top: 0;
}
}
.ttt-tag {
margin: 0.25em 0.5em 0.25em 0;
}
.ttt-link {
.post-link {
display: flex;
flex-direction: row;
align-items: center;
text-align: center;
justify-content: center;
.svg-icon {
height: 1em;
width: 1em;
margin-right: 0.4em;
}
}
.ttt-tag, .ttt-link {
background: white;
color: $gray-700;
border: 1px solid $borderColor;
font-size: 10px;
padding: 2px 5px;
border-radius: $borderRadius;
box-shadow: 0 1px 1px rgba($gray-600, 0.2);
margin: 0.25em 0.5em 0.25em 0;
}
.post-link.tag {
color: $trans-pink-darkest;
border-color: $trans-pink;
}
.post-link.author {
color: $trans-blue-dark;
border-color: $trans-blue;
}
.post-index {
max-width: 1200px;
display: grid;
section { grid-area: articles; }
aside {
h4 { text-align: center; }
.post-tags {
display: flex;
flex-wrap: wrap;
justify-content: stretch;
.post-link {
flex: 1;
flex-basis: $sidebarWidth / 2;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
text-align: center;
}
}
&.authors {
grid-area: authors;
}
&.tags {
grid-area: tags;
}
}
@media (max-width: $articleWidth + $sidebarWidth) {
grid-template-columns: 100%;
grid-row-gap: 1em;
grid-template-areas:
'articles'
'tags'
'authors'
;
aside {
max-width: $articleWidth;
margin: 0 auto;
}
}
@media (min-width: $articleWidth + $sidebarWidth) {
grid-template-columns: $articleWidth 1fr;
grid-column-gap: 1em;
grid-template-areas:
'articles tags'
'articles authors'
;
aside {
position: relative;
.post-tags {
display: grid;
grid-template-columns: repeat( auto-fill, minmax( $sidebarWidth / 2 - 15, 1fr ) );
}
}
}
@media (min-width: $sidebarWidth + $articleWidth + $sidebarWidth) {
grid-template-columns: 1fr $articleWidth 1fr;
grid-column-gap: 1em;
grid-template-areas:
'tags articles authors'
;
}
}
}

View file

@ -3,10 +3,8 @@
position: sticky;
bottom: 0;
display: grid;
grid-template-columns: 1fr 1fr;
grid-gap: 5px;
justify-content: center;
display: flex;
justify-content: space-between;
align-items: center;
padding: 1em 0;
margin-top: -1em;
@ -17,6 +15,7 @@
> div {
display: flex;
flex-basis: 23%;
}
.btn {
@ -24,16 +23,20 @@
box-shadow: 0px 1px 5px rgba(#000, 0.5);
color: white;
white-space: nowrap;
// width: 100%;
min-width: 75%;
width: 100%;
// min-width: 75%;
display: flex;
}
.back, .back .btn {
.btn.left {
justify-content: flex-start;
}
.forward, .forward .btn {
.btn.right {
justify-content: flex-end;
}
span.svg-icon {
margin-top: 1px;
}
}

View file

@ -39,11 +39,14 @@ h4, h5, .h4, .h5 { font-weight: 600; }
$trans-white: #fff;
$trans-pink: #F7A8B8;
$trans-blue: #55CDFC;
$trans-pink-lightest: #fef6f7;
$trans-pink-light: #fde6eb;
$trans-blue-light: #b9eafe;
$trans-blue-lightest: #f0f8ff;
$trans-pink-dark: #ee4b6c;
$trans-blue-dark: #0377a4;
$trans-pink-darkest: #ea1c46;
$trans-blue-darkest: #025372;
$primary: #fc0a7e;
$header-bg-1: #E81179;
@ -87,7 +90,7 @@ $header-full-height: 100px;
// @import "bootstrap/scss/tooltip";
// @import "bootstrap/scss/popover";
// @import "bootstrap/scss/carousel";
// @import "bootstrap/scss/spinners";
@import "bootstrap/scss/spinners";
@import "bootstrap/scss/utilities";
@import "bootstrap/scss/print";

View file

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 448 512"><path fill="currentColor" d="M223.7 239l136-136c9.4-9.4 24.6-9.4 33.9 0l22.6 22.6c9.4 9.4 9.4 24.6 0 33.9L319.9 256l96.4 96.4c9.4 9.4 9.4 24.6 0 33.9L393.7 409c-9.4 9.4-24.6 9.4-33.9 0l-136-136c-9.5-9.4-9.5-24.6-.1-34zm-192 34l136 136c9.4 9.4 24.6 9.4 33.9 0l22.6-22.6c9.4-9.4 9.4-24.6 0-33.9L127.9 256l96.4-96.4c9.4-9.4 9.4-24.6 0-33.9L201.7 103c-9.4-9.4-24.6-9.4-33.9 0l-136 136c-9.5 9.4-9.5 24.6-.1 34z"/></svg>

After

Width:  |  Height:  |  Size: 477 B

View file

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 448 512"><path fill="currentColor" d="M224.3 273l-136 136c-9.4 9.4-24.6 9.4-33.9 0l-22.6-22.6c-9.4-9.4-9.4-24.6 0-33.9l96.4-96.4-96.4-96.4c-9.4-9.4-9.4-24.6 0-33.9L54.3 103c9.4-9.4 24.6-9.4 33.9 0l136 136c9.5 9.4 9.5 24.6.1 34zm192-34l-136-136c-9.4-9.4-24.6-9.4-33.9 0l-22.6 22.6c-9.4 9.4-9.4 24.6 0 33.9l96.4 96.4-96.4 96.4c-9.4 9.4-9.4 24.6 0 33.9l22.6 22.6c9.4 9.4 24.6 9.4 33.9 0l136-136c9.4-9.2 9.4-24.4 0-33.8z"/></svg>

After

Width:  |  Height:  |  Size: 479 B

1
svg/angle-left.svg Normal file
View file

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 256 512"><path fill="currentColor" d="M31.7 239l136-136c9.4-9.4 24.6-9.4 33.9 0l22.6 22.6c9.4 9.4 9.4 24.6 0 33.9L127.9 256l96.4 96.4c9.4 9.4 9.4 24.6 0 33.9L201.7 409c-9.4 9.4-24.6 9.4-33.9 0l-136-136c-9.5-9.4-9.5-24.6-.1-34z"/></svg>

After

Width:  |  Height:  |  Size: 289 B

1
svg/angle-right.svg Normal file
View file

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 256 512"><path fill="currentColor" d="M224.3 273l-136 136c-9.4 9.4-24.6 9.4-33.9 0l-22.6-22.6c-9.4-9.4-9.4-24.6 0-33.9l96.4-96.4-96.4-96.4c-9.4-9.4-9.4-24.6 0-33.9L54.3 103c9.4-9.4 24.6-9.4 33.9 0l136 136c9.5 9.4 9.5 24.6.1 34z"/></svg>

After

Width:  |  Height:  |  Size: 290 B

View file

@ -1 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 448 512"><path fill="currentColor" d="M207.029 381.476L12.686 187.132c-9.373-9.373-9.373-24.569 0-33.941l22.667-22.667c9.357-9.357 24.522-9.375 33.901-.04L224 284.505l154.745-154.021c9.379-9.335 24.544-9.317 33.901.04l22.667 22.667c9.373 9.373 9.373 24.569 0 33.941L240.971 381.476c-9.373 9.372-24.569 9.372-33.942 0z"/></svg>

Before

Width:  |  Height:  |  Size: 380 B

View file

@ -1 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 320 512"><path fill="currentColor" d="M34.52 239.03L228.87 44.69c9.37-9.37 24.57-9.37 33.94 0l22.67 22.67c9.36 9.36 9.37 24.52.04 33.9L131.49 256l154.02 154.75c9.34 9.38 9.32 24.54-.04 33.9l-22.67 22.67c-9.37 9.37-24.57 9.37-33.94 0L34.52 272.97c-9.37-9.37-9.37-24.57 0-33.94z"/></svg>

Before

Width:  |  Height:  |  Size: 339 B

View file

@ -1 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 320 512"><path fill="currentColor" d="M285.476 272.971L91.132 467.314c-9.373 9.373-24.569 9.373-33.941 0l-22.667-22.667c-9.357-9.357-9.375-24.522-.04-33.901L188.505 256 34.484 101.255c-9.335-9.379-9.317-24.544.04-33.901l22.667-22.667c9.373-9.373 24.569-9.373 33.941 0L285.475 239.03c9.373 9.372 9.373 24.568.001 33.941z"/></svg>

Before

Width:  |  Height:  |  Size: 382 B

View file

@ -1 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 448 512"><path fill="currentColor" d="M240.971 130.524l194.343 194.343c9.373 9.373 9.373 24.569 0 33.941l-22.667 22.667c-9.357 9.357-24.522 9.375-33.901.04L224 227.495 69.255 381.516c-9.379 9.335-24.544 9.317-33.901-.04l-22.667-22.667c-9.373-9.373-9.373-24.569 0-33.941L207.03 130.525c9.372-9.373 24.568-9.373 33.941-.001z"/></svg>

Before

Width:  |  Height:  |  Size: 385 B

View file

@ -1 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><path d="M326.612 185.391c59.747 59.809 58.927 155.698.36 214.59-.11.12-.24.25-.36.37l-67.2 67.2c-59.27 59.27-155.699 59.262-214.96 0-59.27-59.26-59.27-155.7 0-214.96l37.106-37.106c9.84-9.84 26.786-3.3 27.294 10.606.648 17.722 3.826 35.527 9.69 52.721 1.986 5.822.567 12.262-3.783 16.612l-13.087 13.087c-28.026 28.026-28.905 73.66-1.155 101.96 28.024 28.579 74.086 28.749 102.325.51l67.2-67.19c28.191-28.191 28.073-73.757 0-101.83-3.701-3.694-7.429-6.564-10.341-8.569a16.037 16.037 0 0 1-6.947-12.606c-.396-10.567 3.348-21.456 11.698-29.806l21.054-21.055c5.521-5.521 14.182-6.199 20.584-1.731a152.482 152.482 0 0 1 20.522 17.197zM467.547 44.449c-59.261-59.262-155.69-59.27-214.96 0l-67.2 67.2c-.12.12-.25.25-.36.37-58.566 58.892-59.387 154.781.36 214.59a152.454 152.454 0 0 0 20.521 17.196c6.402 4.468 15.064 3.789 20.584-1.731l21.054-21.055c8.35-8.35 12.094-19.239 11.698-29.806a16.037 16.037 0 0 0-6.947-12.606c-2.912-2.005-6.64-4.875-10.341-8.569-28.073-28.073-28.191-73.639 0-101.83l67.2-67.19c28.239-28.239 74.3-28.069 102.325.51 27.75 28.3 26.872 73.934-1.155 101.96l-13.087 13.087c-4.35 4.35-5.769 10.79-3.783 16.612 5.864 17.194 9.042 34.999 9.69 52.721.509 13.906 17.454 20.446 27.294 10.606l37.106-37.106c59.271-59.259 59.271-155.699.001-214.959z"/></svg>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><path fill="currentColor" d="M326.612 185.391c59.747 59.809 58.927 155.698.36 214.59-.11.12-.24.25-.36.37l-67.2 67.2c-59.27 59.27-155.699 59.262-214.96 0-59.27-59.26-59.27-155.7 0-214.96l37.106-37.106c9.84-9.84 26.786-3.3 27.294 10.606.648 17.722 3.826 35.527 9.69 52.721 1.986 5.822.567 12.262-3.783 16.612l-13.087 13.087c-28.026 28.026-28.905 73.66-1.155 101.96 28.024 28.579 74.086 28.749 102.325.51l67.2-67.19c28.191-28.191 28.073-73.757 0-101.83-3.701-3.694-7.429-6.564-10.341-8.569a16.037 16.037 0 0 1-6.947-12.606c-.396-10.567 3.348-21.456 11.698-29.806l21.054-21.055c5.521-5.521 14.182-6.199 20.584-1.731a152.482 152.482 0 0 1 20.522 17.197zM467.547 44.449c-59.261-59.262-155.69-59.27-214.96 0l-67.2 67.2c-.12.12-.25.25-.36.37-58.566 58.892-59.387 154.781.36 214.59a152.454 152.454 0 0 0 20.521 17.196c6.402 4.468 15.064 3.789 20.584-1.731l21.054-21.055c8.35-8.35 12.094-19.239 11.698-29.806a16.037 16.037 0 0 0-6.947-12.606c-2.912-2.005-6.64-4.875-10.341-8.569-28.073-28.073-28.191-73.639 0-101.83l67.2-67.19c28.239-28.239 74.3-28.069 102.325.51 27.75 28.3 26.872 73.934-1.155 101.96l-13.087 13.087c-4.35 4.35-5.769 10.79-3.783 16.612 5.864 17.194 9.042 34.999 9.69 52.721.509 13.906 17.454 20.446 27.294 10.606l37.106-37.106c59.271-59.259 59.271-155.699.001-214.959z"/></svg>

Before

Width:  |  Height:  |  Size: 1.3 KiB

After

Width:  |  Height:  |  Size: 1.3 KiB

View file

@ -1 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 448 512"><path d="M448 48v32a16 16 0 0 1-16 16h-48v368a16 16 0 0 1-16 16h-32a16 16 0 0 1-16-16V96h-32v368a16 16 0 0 1-16 16h-32a16 16 0 0 1-16-16V352h-32a160 160 0 0 1 0-320h240a16 16 0 0 1 16 16z"/></svg>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 448 512"><path fill="currentColor" d="M448 48v32a16 16 0 0 1-16 16h-48v368a16 16 0 0 1-16 16h-32a16 16 0 0 1-16-16V96h-32v368a16 16 0 0 1-16 16h-32a16 16 0 0 1-16-16V352h-32a160 160 0 0 1 0-320h240a16 16 0 0 1 16 16z"/></svg>

Before

Width:  |  Height:  |  Size: 258 B

After

Width:  |  Height:  |  Size: 279 B

1
svg/sync-alt.svg Normal file
View file

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><path fill="currentColor" d="M370.72 133.28C339.458 104.008 298.888 87.962 255.848 88c-77.458.068-144.328 53.178-162.791 126.85-1.344 5.363-6.122 9.15-11.651 9.15H24.103c-7.498 0-13.194-6.807-11.807-14.176C33.933 94.924 134.813 8 256 8c66.448 0 126.791 26.136 171.315 68.685L463.03 40.97C478.149 25.851 504 36.559 504 57.941V192c0 13.255-10.745 24-24 24H345.941c-21.382 0-32.09-25.851-16.971-40.971l41.75-41.749zM32 296h134.059c21.382 0 32.09 25.851 16.971 40.971l-41.75 41.75c31.262 29.273 71.835 45.319 114.876 45.28 77.418-.07 144.315-53.144 162.787-126.849 1.344-5.363 6.122-9.15 11.651-9.15h57.304c7.498 0 13.194 6.807 11.807 14.176C478.067 417.076 377.187 504 256 504c-66.448 0-126.791-26.136-171.315-68.685L48.97 471.03C33.851 486.149 8 475.441 8 454.059V320c0-13.255 10.745-24 24-24z"/></svg>

After

Width:  |  Height:  |  Size: 863 B

View file

@ -22,7 +22,9 @@
{{#content "body"}}
<article class="markup">
<div class="post-content">{{{contents}}}</div>
<div class="post-content">
{{{contents}}}
</div>
</article>
{{/content}}

47
templates/post.hbs Normal file
View file

@ -0,0 +1,47 @@
{{#extend "layout"}}
{{#content "meta"}}
<meta property="og:title" content="{{meta.title}}{{#if meta.subtitle}}, {{meta.subtitle}}{{/if}}">
<meta property="og:description" content="{{meta.description}}">
{{#if titlecard}}<meta property="og:image" content="https://{{page.domain}}{{rev titlecard}}">{{/if}}
<meta property="og:url" content="https://{{page.domain}}{{url}}">
<meta name="twitter:title" content="{{meta.title}}{{#if meta.subtitle}}, {{meta.subtitle}}{{/if}}">
<meta name="twitter:description" content="{{meta.description}}">
{{#if titlecard}}
<meta name="twitter:card" content="summary_large_image">
<meta name="twitter:image" content="https://{{page.domain}}{{rev titlecard}}">
{{/if}}
<link rel="canonical" href="{{#if canonical}}{{canonical}}{{else}}https://{{page.domain}}{{url}}{{/if}}">
{{/content}}
{{#append 'post-header'}}
{{#if meta.preBody}}{{import meta.preBody}}{{/if}}
{{/append}}
{{#content "bodyClass"}}{{#each classes}}{{this}} {{/each}}post{{/content}}
{{#content "body"}}
<div class="disclaimer"><div class="container" style="text-align: center">
<h1>Trans Twitter Topics</h1>
<strong>A collection of the best Twitter threads on transgender and gender dysphoria topics.</strong>
<p>Have a thread that you think belongs here? <a href="https://twitter.com/TwippingVanilla">Send it my way</a>.</p>
</div></div>
<article>
<div class="post-head">
<div class="post-tags">
{{#each meta.tags}}<a href="/tweets/#tag={{@key}}" class="post-link tag">{{this}}</a>{{/each}}
{{#each meta.author}}<a href="/tweets/#author={{this}}" class="post-link author">{{this}}</a>{{/each}}
</div>
<a href="{{url}}" class="post-link" title="{{date dateCreated 'MMMM do, yyyy'}}">{{icon 'link'}} Permalink</a>
</div>
<div class="post-content markup">{{{contents}}}</div>
<div class="pager">
<div class="prev">{{#if siblings.prev}}<a href="{{siblings.prev}}" class="btn btn-primary left">{{icon 'angle-left'}} Back</a>{{/if}}</div>
<div class="first">{{#if siblings.first}}<a href="{{siblings.first}}" class="btn btn-primary left">{{icon 'angle-double-left'}} Newest</a>{{/if}}</div>
<div class="last">{{#if siblings.last}}<a href="{{siblings.last}}" class="btn btn-primary right">Oldest {{icon 'angle-double-right'}}</a>{{/if}}</div>
<div class="next">{{#if siblings.next}}<a href="{{siblings.next}}" class="btn btn-primary right">Next {{icon 'angle-right'}}</a>{{/if}}</div>
</div>
</article>
{{/content}}
{{/extend}}