commented code, added some info, added icon

This commit is contained in:
xananax prozaxx 2022-07-03 00:21:20 +02:00
parent b4fd4f344c
commit 90449faa8b
8 changed files with 375 additions and 146 deletions

BIN
favicon.ico Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

69
favicon.svg Normal file
View File

@ -0,0 +1,69 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
width="16"
height="16"
viewBox="0 0 16 16"
version="1.1"
id="svg5"
inkscape:version="1.2 (dc2aedaf03, 2022-05-15)"
sodipodi:docname="favicon.svg"
inkscape:export-filename="favicon.png"
inkscape:export-xdpi="96"
inkscape:export-ydpi="96"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg">
<sodipodi:namedview
id="namedview7"
pagecolor="#ffffff"
bordercolor="#111111"
borderopacity="1"
inkscape:showpageshadow="0"
inkscape:pageopacity="0"
inkscape:pagecheckerboard="1"
inkscape:deskcolor="#d1d1d1"
inkscape:document-units="mm"
showgrid="false"
inkscape:zoom="12.500449"
inkscape:cx="-10.359628"
inkscape:cy="-1.3599512"
inkscape:window-width="1894"
inkscape:window-height="1027"
inkscape:window-x="12"
inkscape:window-y="39"
inkscape:window-maximized="1"
inkscape:current-layer="layer1" />
<defs
id="defs2" />
<g
inkscape:label="Layer 1"
inkscape:groupmode="layer"
id="layer1">
<path
style="fill:none;stroke:#000000;stroke-width:0;stroke-linecap:butt;stroke-linejoin:miter;stroke-dasharray:none;stroke-opacity:1"
d="m 12.162856,1.1802782 c 0,0 1.007154,8.1860204 -2.5655481,11.2237428 -3.5727018,3.037721 -6.1129243,3.538782 -6.1129243,3.538782"
id="path186" />
<path
id="path992"
style="fill:#af31f2;fill-opacity:1;stroke:none;stroke-width:0.377953;stroke-linecap:butt;stroke-linejoin:miter;stroke-dasharray:none;stroke-opacity:1"
d="M 13.421875 0.22265625 C 13.243019 0.22699692 13.048806 0.2478116 12.837891 0.28515625 C 11.29629 0.19693637 10.640413 1.902967 10.0625 3.7285156 C 10.593054 4.5272931 10.951884 5.6138429 11.332031 6.6640625 C 10.771084 6.1649362 10.18045 5.7078217 9.5605469 5.2910156 C 9.3375301 5.9445552 9.0940197 6.5507321 8.7871094 7.0175781 C 8.2557966 7.9396061 7.4762392 8.7292399 6.7988281 9.4589844 C 7.2517186 10.440809 7.4773187 11.536722 7.3632812 12.802734 C 7.0445545 11.904073 6.7417952 11.001143 6.1523438 10.193359 C 5.9826711 10.403058 5.8319845 10.609145 5.7128906 10.814453 C 5.0621036 11.936356 5.8828125 13.615234 5.8828125 13.615234 L 5.4296875 14.050781 C 5.4296875 14.050781 7.1204326 14.419924 8.5742188 13.962891 C 9.408252 13.700692 10.419951 12.721913 11.404297 11.460938 C 10.870816 11.207163 10.317939 11.02845 9.6738281 10.757812 C 10.633436 10.549609 11.455468 10.331412 12.3125 10.212891 C 12.721237 9.6110451 13.111537 8.9802434 13.470703 8.3574219 C 14.273698 6.5920356 17.058609 0.1343959 13.421875 0.22265625 z " />
<path
id="path1363"
style="fill:#ca67ff;fill-opacity:1;stroke:none;stroke-width:0.377953;stroke-linecap:butt;stroke-linejoin:miter;stroke-dasharray:none;stroke-opacity:1"
d="M 10.335938,2.9140625 C 10.241749,3.1799179 10.150264,3.451282 10.0625,3.7285156 10.593053,4.5272923 10.951885,5.613844 11.332031,6.6640625 10.771085,6.1649367 10.180449,5.7078213 9.5605469,5.2910156 9.3375303,5.9445546 9.0940194,6.5507326 8.7871094,7.0175781 8.2557971,7.9396052 7.4762385,8.7292406 6.7988281,9.4589844 7.2517182,10.440808 7.4773186,11.536724 7.3632812,12.802734 7.0445549,11.904074 6.7417946,11.001143 6.1523438,10.193359 c -0.1696726,0.209699 -0.3203594,0.415786 -0.4394532,0.621094 -0.6507863,1.121902 0.1699219,2.800781 0.1699219,2.800781 l -0.453125,0.435547 c 0,0 1.6907465,0.369142 3.1445313,-0.08789 0.8340323,-0.262199 1.8457332,-1.24098 2.8300782,-2.501953 C 10.870816,11.207163 10.317938,11.02845 9.6738281,10.757812 10.633435,10.54961 11.455469,10.331412 12.3125,10.212891 12.721237,9.6110453 13.111537,8.9802428 13.470703,8.3574219 13.797972,7.6379211 14.450409,6.1386111 14.867188,4.6210938 14.084503,4.5508119 13.187397,4.5444586 12.134766,4.625 11.431614,4.1846627 10.879527,3.5546956 10.335938,2.9140625 Z"
sodipodi:nodetypes="ccccccccsccsccccccc" />
<path
style="fill:#ca67ff;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
d="M 5.5668235,14.115081 C 5.2893119,13.070423 4.632697,13.050621 3.9714324,12.734186 c 0.3451709,0.687375 0.5960759,1.065893 0.6878635,1.822365 z"
id="path1148"
sodipodi:nodetypes="cccc" />
<path
style="fill:#52007e;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
d="M 1.839835,15.713619 C 5.4799804,13.496511 8.9758758,13.924266 12.74917,2.8965675 12.016939,5.9970442 10.139599,13.063588 2.0854671,15.900352 Z"
id="path1150"
sodipodi:nodetypes="cccc" />
</g>
</svg>

After

Width:  |  Height:  |  Size: 4.8 KiB

View File

@ -1,2 +1,3 @@
readme.md 2022-28-06 Tickle readme.md 2022-28-06 Tickle
examples.md 2022-28-06 Examples examples.md 2022-28-06 Examples
tutorial-styling.md 2022-28-06 Styling

View File

@ -22,15 +22,89 @@
margin: 0; margin: 0;
padding: 0; padding: 0;
} }
.trigger { body {
display: none; padding: 3em;
max-width: 52em;
margin: 0 auto;
} }
.left-nav > * { pre {
margin: 0;
padding: 3em;
max-width: inherit;
max-height: 100vh;
overflow: scroll;
background: rgb(192, 192, 192);
position: absolute;
top: 0;
left: 50%;
transition: all 1s cubic-bezier(0.68, -0.55, 0.265, 1.55);
transform: translate(-50%, -100%);
}
.is-source-enabled pre {
transform: translate(-50%, 0);
}
button {
display: inline-block;
border: none;
margin: 0;
text-decoration: none;
font-family: inherit;
font-size: 1rem;
display: flex;
justify-content: center;
align-content: center;
text-align: center;
vertical-align: middle;
background: var(--accent);
color: var(--background);
cursor: pointer;
padding: 0.5em 1em;
border-radius: 0 0 0.2em 0;
box-shadow: rgba(149, 157, 165, 0.2) 0px 8px 24px;
-webkit-appearance: none;
-moz-appearance: none;
}
h1,
h2,
h3,
h4 {
color: var(--accent);
font-family: "Lobster", serif;
}
a {
color: var(--accent);
position: relative;
text-decoration: none;
padding: 0.1em;
}
a::after {
content: "";
position: absolute;
background-color: var(--accent);
position: absolute;
left: 0;
bottom: 3px;
width: 100%;
height: 1px;
z-index: -1;
transition: all 0.1s ease-in;
}
a:hover {
color: var(--background);
transition: all 0.3s ease-in-out;
}
a:hover::after {
bottom: 0;
height: 100%;
transition: all 0.3s ease-in-out;
}
.menu,
.burger {
position: fixed; position: fixed;
top: 0; top: 0;
left: 0; left: 0;
} }
.left-nav > .menu { .menu {
padding: 1em; padding: 1em;
transform: translateX(-100%); transform: translateX(-100%);
display: flex; display: flex;
@ -40,39 +114,26 @@
transition: all 0.3s ease-out; transition: all 0.3s ease-out;
gap: 1em; gap: 1em;
} }
.left-nav > .menu a { .menu a {
text-decoration: none; text-decoration: none;
background: var(--background); background: var(--background);
color: var(--accent); color: var(--accent);
box-shadow: rgba(100, 100, 111, 0.2) 0px 7px 29px 0px; box-shadow: rgba(100, 100, 111, 0.2) 0px 7px 29px 0px;
padding: 0.2em 0.4em; padding: 0.2em 0.4em;
} }
.left-nav > .menu a:hover { .menu a:hover {
background: var(--accent); background: var(--accent);
color: var(--background); color: var(--background);
} }
.left-nav > .trigger:checked ~ .menu { .menu-is-open .menu {
transform: translateX(0); transform: translateX(0);
transition-duration: 0.1s; transition-duration: 0.1s;
} }
.burger { .menu-is-open .burger {
width: 2em;
height: 2em;
display: flex;
justify-content: center;
align-content: center;
text-align: center;
vertical-align: middle;
line-height: 2em;
border-radius: 0.2em;
box-shadow: rgba(149, 157, 165, 0.2) 0px 8px 24px;
transition: background 0.5s ease;
}
.left-nav > .trigger:checked ~ .burger {
color: transparent; color: transparent;
} }
.left-nav > .trigger:checked ~ .burger, .menu-is-open .burger,
#Loading { #Loading {
top: 0; top: 0;
right: 0; right: 0;
@ -80,9 +141,10 @@
height: 100%; height: 100%;
border-radius: 0; border-radius: 0;
background: rgba(17, 17, 17, 0.2); background: rgba(17, 17, 17, 0.2);
cursor: default;
} }
#Loading { #Loading {
position: absolute; position: fixed;
text-align: center; text-align: center;
align-items: center; align-items: center;
justify-content: center; justify-content: center;
@ -94,16 +156,15 @@
display: flex; display: flex;
overflow: hidden; overflow: hidden;
} }
#Loading > * {
animation: load 1.2s infinite cubic-bezier(0.215, 0.61, 0.355, 1);
}
.is-loading #Loading { .is-loading #Loading {
opacity: 1; opacity: 1;
height: 100%; height: 100%;
transition: opacity 1s ease-in, height 0 linear; transition: opacity 1s ease-in, height 0 linear;
} }
#Loading::after { @keyframes load {
content: "❤";
animation: beat 1.2s infinite cubic-bezier(0.215, 0.61, 0.355, 1);
}
@keyframes beat {
0% { 0% {
transform: scale(0.95); transform: scale(0.95);
} }
@ -123,48 +184,37 @@
transform: scale(0.9); transform: scale(0.9);
} }
} }
#Body {
padding: 3em;
max-width: 52em;
margin: 0 auto;
}
#Body h1,
#Body h2,
#Body h3,
#Body h4 {
color: var(--accent);
font-family: "Lobster", serif;
}
#Body a {
color: var(--background);
background-color: var(--accent);
text-decoration: none;
padding: 0.1em 0.4em;
}
</style> </style>
</head> </head>
<body> <body>
<nav class="left-nav"> <nav class="left-nav">
<input id="main-nav" type="checkbox" class="trigger" /> <button id="Burger" class="burger">&#8801;</button>
<label for="main-nav" class="burger">&#8801;</label>
<div id="Menu" class="menu"></div> <div id="Menu" class="menu"></div>
</nav> </nav>
<header id="Menu"></header>
<main id="Body"></main> <main id="Body"></main>
<div id="Loading"></div> <pre id="Source"></pre>
<div id="Loading">
<p></p>
</div>
<script type="module"> <script type="module">
//@ts-check //@ts-check
const is_debug_mode = /^localhost|127.0.0.1/.test( /*****************************************************************
window.location.hostname *
); * "THE FRAMEWORK"
* a small set of utilities that help
*
****************************************************************/
/** /**
* markdown parser. Remove if you don't use markdown * markdown parser. Remove if you don't use markdown
*/ */
// @ts-ignore // @ts-ignore
import { micromark } from "https://esm.sh/micromark@3?bundle"; import { micromark } from "https://esm.sh/micromark@3?bundle";
const is_debug_mode = /^localhost|127.0.0.1/.test(
window.location.hostname
);
/** /**
* A small utility to query elements and get back an array * A small utility to query elements and get back an array
*/ */
@ -179,33 +229,120 @@
* @param {string} url * @param {string} url
*/ */
const isExternal = (url) => const isExternal = (url) =>
/^(https?|mailto|tel|ftp|ipfs|dat):/.test(url); url && /^(https?|mailto|tel|ftp|ipfs|dat):/.test(url);
/**
* Makes sure urls to local pages get passed through the routing system
* @param {HTMLElement} container the element containing links to find
*/
const rewriteLocalUrls = (container) => { const rewriteLocalUrls = (container) => {
$(container, "a").forEach((a) => { $(container, "a").forEach((a) => {
const href = a.getAttribute("href"); const href = a.getAttribute("href");
if (href && !isExternal(href)) { if (href && !isExternal(href)) {
console.log({ href }); a.setAttribute("href", "#/" + href.replace(/^\.?\//, ""));
const rewrittenHref = "#/" + href.replace(/^\.?\//, "");
a.setAttribute("href", rewrittenHref);
console.log(a);
console.log(rewrittenHref);
} }
}); });
return container;
};
/**
* Returns the hash part of the url, but only if it starts with a `/`
* This allows regular hashes to continue to work
* It reads also query parameters
*/
const getCurrentHashUrl = () => {
const [path, searchStr] = (
window.location.hash[1] === "/" ? window.location.hash.slice(2) : ""
).split("?");
const params = new URLSearchParams(searchStr);
return { path, params };
}; };
/** /**
* useful to check for transitions while developing styles, if the loading screen disappears too fast * useful to check for transitions while developing styles, if the loading screen disappears too fast
* * uses micromark. You can plug a different parser if you prefer
*/ */
const wait = (val) => new Promise((ok) => setTimeout(ok, 1, val)); const wait = is_debug_mode
? (val) => new Promise((ok) => setTimeout(ok, 1000, val))
: (val) => val;
/**
* @param {string} htmlString
*/
const generateDOM = (htmlString) =>
/** @type {HTMLElement} */ (
new DOMParser().parseFromString(
`<div>${htmlString}</div>`,
"text/html"
).firstChild
);
/**
* Loads and parses a markdown document. Makes use of micromark
* @param {string} path the path to load
*/
const loadMarkdown = (path) =>
fetch(is_debug_mode ? `./${path}?rand=${Math.random()}` : `./${path}`)
.then((response) => response.text())
.then(wait)
.then((raw) => {
const content = rewriteLocalUrls(generateDOM(micromark(raw)));
const firstTitleElement = content.querySelector("h1");
const title = firstTitleElement
? firstTitleElement.textContent
: path.replace(/\.\w{2, 4}$/, "");
return { title, raw, content };
});
/**
* parses a filelist string. That's a string that looks like
* ```
* name dd-mm-yyyy link name
* name dd-mm-yyyy
* name linkname
* name
* ```
* @param {string} lines
*/
const parseFileList = (lines) =>
lines
.trim()
.split(`\n`)
.map((line, index) => {
const today = new Date().toISOString().split("T")[0];
const result = line.match(
/(?<name>.+)\.(?<ext>\w{2,3})(?:\s+(?<date>[\d-]+)?(?<title>.+))?/
);
if (!result) {
console.error(`could not parse line ${index}: [${line}]`);
return null;
}
const {
groups: { name, ext, date = today, title = name },
} = result;
const href = `/${name}.${ext}`;
const date_unix = new Date(date).getTime();
return { name, href, date, title, date_unix, index };
})
.filter(Boolean)
.sort(({ date_unix: a }, { date_unix: b }) => a - b);
/*****************************************************************
*
* Creating references to the important stuff
*
****************************************************************/
/** /**
* The elements we will need * The elements we will need
*/ */
const [Menu, Body, Loading] = ["Menu", "Body", "Loading"].map((id) => const [Menu, Body, Loading, Source, Burger] = [
document.getElementById(id) "Menu",
); "Body",
"Loading",
"Source",
"Burger",
].map((id) => document.getElementById(id));
/** /**
* cache the original title to append it to the page title * cache the original title to append it to the page title
@ -217,85 +354,86 @@
const hideLoadingOverlay = () => const hideLoadingOverlay = () =>
document.body.classList.remove("is-loading"); document.body.classList.remove("is-loading");
const getCurrentHashUrl = () => { /*****************************************************************
const [path, searchStr] = ( *
window.location.hash[1] === "/" ? window.location.hash.slice(2) : "" * Router
).split("?"); *
const params = new URLSearchParams(searchStr); * Things related to main navigation and to loading pages
return { path, params }; *
}; ****************************************************************/
const onHashChange = (evt) => { /**
* Listens to hashchange event, and attempts to read the url.
* If the url is set,
*/
const onHashChange = async (evt) => {
const { path, params } = getCurrentHashUrl(); const { path, params } = getCurrentHashUrl();
if (!path) { if (!path) {
return false; return false;
} }
showLoadingOverlay(); showLoadingOverlay();
return fetch( const { title, raw, content } = await loadMarkdown(path);
is_debug_mode ? `./${path}?rand=${Math.random()}` : `./${path}` document.title = `${title} | ${mainTitle}`;
) Body.innerHTML = "";
.then((response) => response.text()) Source.innerHTML = raw;
.then(wait) Body.appendChild(content);
.then((text) => { hideLoadingOverlay();
const [, title] = text.match(/^#\s+(\w+.+)/) ||
text.match(/(.*?)\n===+/m) || [, path];
document.title = `${title} | ${mainTitle}`;
if (params.has("source")) {
Body.innerHTML = `<pre>${text}</pre>`;
} else {
Body.innerHTML = micromark(text);
rewriteLocalUrls(Body);
}
hideLoadingOverlay();
});
}; };
window.addEventListener("hashchange", onHashChange); /**
* Called when the file list is obtained (presumably through loading)
* parses the file list, then fills the side navigation
* If there's no page loaded, it also loads the first page in the list
* (the list gets sorted by date, but the first line is the one that gets used)
* @param {string} lines
*/
const fillMenuFromFileList = (lines) => {
const links = parseFileList(lines);
Menu.innerHTML = links
.map(({ href, title }) => `<a data-link href="#${href}">${title}</a>`)
.join(`\n`);
if (!getCurrentHashUrl().path) {
const href = links.find(({ index }) => index === 0).href;
window.location.hash = `#${href}`;
} else {
onHashChange();
}
};
/*****************************************************************
*
* Bootstrapping
*
* this is where things actually happen
*
****************************************************************/
const loadFileList = () => {
showLoadingOverlay();
fetch("./files.txt")
.then((response) => response.text())
.then(fillMenuFromFileList);
};
/** /**
* Loads the article list, parses it, creates the menu items * Loads the article list, parses it, creates the menu items
*/ */
const start = () => { (function bootstrap() {
let hasAutoloadedFirstPage = false; Burger.addEventListener("click", () =>
showLoadingOverlay(); document.body.classList.toggle("menu-is-open")
fetch("./files.txt") );
.then((response) => response.text())
.then((lines) => {
Menu.innerHTML = lines
.split(`\n`)
.map((line, index) => {
const today = new Date().toISOString().split("T")[0];
const result = line.match(
/(?<name>.+)\.(?<ext>\w{2,3})(?:\s+(?<date>[\d-]+)?(?<title>.+))?/
);
if (!result) {
console.log(`could not parse line ${index}: [${line}]`);
return null;
}
const {
groups: { name, ext, date = today, title = name },
} = result;
const href = `/${name}.${ext}`;
if (!getCurrentHashUrl().path) {
hasAutoloadedFirstPage = true;
window.location.hash = `#${href}`;
}
const date_unix = new Date(date).getTime();
return { name, href, date, title, date_unix };
})
.filter(Boolean)
.sort(({ date_unix: a }, { date_unix: b }) => a - b)
.map(
({ href, title }) => `<a data-link href="#${href}">${title}</a>`
)
.join(`\n`);
if (!hasAutoloadedFirstPage) {
onHashChange();
}
});
};
start(); window.addEventListener("hashchange", onHashChange);
loadFileList();
document.addEventListener(
"keyup",
({ key }) =>
key === "?" && document.body.classList.toggle("is-source-enabled")
);
})();
</script> </script>
</body> </body>
</html> </html>

View File

@ -15,7 +15,7 @@ By default, Tickle will:
1. Read a text file containing a list of posts 1. Read a text file containing a list of posts
2. Load the first post 2. Load the first post
3. Parse the markdown 3. Render the markdown
4. Display the list of links, and the pages 4. Display the list of links, and the pages
Tickle is small enough that a moderately knowledgeable programmer should be able to make it do whatever they want. Tickle is small enough that a moderately knowledgeable programmer should be able to make it do whatever they want.
@ -24,10 +24,10 @@ Tickle is small enough that a moderately knowledgeable programmer should be able
## How do I use Tickle? ## How do I use Tickle?
1. write markdown files 1. write markdown files
2. reference your files in a text file called "files.txt" 2. Optional: reference files in a text file called "files.txt" to fill the side navigation
3. upload everything on some server 3. upload everything on some server
We have a [neat little UI to help you do all of that](https://git.poto.cafe/yagich/ticle-godot-frontend). You don't have to use it, but it's neat. We have a [neat little UI to help you do all of that](https://git.poto.cafe/yagich/tickle-godot-frontend). You don't have to use it, but it's neat.
Here are more detailed explanations, for different knowledge levels. Here are more detailed explanations, for different knowledge levels.
@ -45,7 +45,9 @@ It's important to know that Tickle is an *idea*. Think of it as a specification.
### Q: What if I want custom functionality, but don't know programming at all? ### Q: What if I want custom functionality, but don't know programming at all?
**A**: Check the [examples](examples.md) first, and see if there's something for you. Otherwise, write to us! Find us on X or Y, and shoot your request. Maybe we'll get to it **A**: Check the [examples](examples.md) first, and see if there's something for you. Otherwise, write to us! Find us on X or Y, and shoot your request. Maybe we'll get to it.
TODO: specify where to find us
### Q: Everything in the browser? Isn't that bad for SEO? ### Q: Everything in the browser? Isn't that bad for SEO?
@ -53,7 +55,7 @@ It's important to know that Tickle is an *idea*. Think of it as a specification.
### Q: Isn't it bad for performance? ### Q: Isn't it bad for performance?
**A**: Again, who cares? For Facebook, a microsecond lost means a large amount of people will not see ads fast enough, that loses them money. But someone who comes to read your blog will wait 300ms to read your article, no worries. **A**: Again, who cares? For Facebook, a microsecond could mean a large amount of people will miss some ads, and that costs them profits. But someone who comes to read your blog will wait 300ms to read your article, no worries.
### Q: What about accessibility? ### Q: What about accessibility?
@ -61,7 +63,9 @@ It's important to know that Tickle is an *idea*. Think of it as a specification.
### Q: What's the license? ### Q: What's the license?
**A**: The license is [[upcoming]] **A**: The license is upcoming
TODO: decide on a license
### Q: "Tickle"? ### Q: "Tickle"?

View File

@ -6,4 +6,6 @@ Tickle consists of 3 distinct and simple parts:
2. parses markdown files in the browser 2. parses markdown files in the browser
3. displays the markdown files 3. displays the markdown files
To parse markdown files, the default implementation uses Micromark, but feel free to plug any parser you like. To parse markdown files, the default implementation uses Micromark, but feel free to plug any parser you like.
The source is commented fairly well, and your best bet is to check out the [examples](examples.md).

View File

@ -5,20 +5,24 @@ Unfortunately, we couldn't remove entirely all the technicalities. But we've man
There are 4 steps to writing articles in Tickle: There are 4 steps to writing articles in Tickle:
1. Write the articles in Markdown 1. Write the articles in Markdown
2. List the articles in a file 2. Optionally, list the articles in a file
3. Optionally, preview the site locally before uploading 3. Optionally, preview the site locally before uploading
4. Upload everything on a server 4. Upload everything on a server
We have a desktop app that takes care of steps 1, 2, and 3 (and maybe some day step 4!). It's comfy, made with love, and you can install it from https://git.poto.cafe/yagich/ticle-godot-frontend We have a desktop app that takes care of steps 1, 2, and 3 (and maybe some day step 4!). It's comfy, made with love, and you can install it from https://git.poto.cafe/yagich/tickle-godot-frontend
But if you want to do the things manually, read on. You need to do step 4 manually either way. But if you want to do the things manually, read on (You need to do step 4 manually either way).
## 1. Write A Few Articles ## 1. Write A Few Articles
You will need some knowledge of Markdown. This is how we write the articles. It's really not hard! In fact, it looks very similar to how you already use characters in chat for emphasis. Check [a reference here](https://commonmark.org/help/). Create a document, call it "my-first-article.md" for example (`.md` stands for "markdown"). Write some text in it. You will need some knowledge of Markdown. This is how we write the articles. It's really not hard! In fact, it looks very similar to how you already use characters in chat for emphasis. Check [a reference here](https://commonmark.org/help/).
For example: create a document, call it "my-first-article.md" for example (`.md` stands for "markdown"). Write some text in it. Want an example of markdown? Press `?` on any Tickle page in any Tickle site to display the source.
## 2. Create the List of Files ## 2. Create the List of Files
This is optional, and is used to fill the side navigation. Without this step though, your site will be empty, so you need to write at least one page in it.
Once you have written a document, you then open a new file, call it "files.txt". At the top, write: Once you have written a document, you then open a new file, call it "files.txt". At the top, write:
``` ```
@ -31,9 +35,7 @@ At this state, the site is "ready". You can upload it, but before that, you prob
## 3. Preview the site ## 3. Preview the site
You will need to run a server. The easier server to run is [our own](https://git.poto.cafe/yagich/ticle-godot-frontend), but if you want other options, you want to search for "easy http server", and see what you get. You will need to run a server. The easier server to run is [our own](https://git.poto.cafe/yagich/ticle-godot-frontend), but if you want other options, [XAMPP](https://www.apachefriends.org/download.html) is a good one (but is not specialized for Tickle).
- TODO: list a few good options here
Once you have the server up and running, you can open a browser to the address the site opened. Usually, something like http://localhost:3000. The number is arbitrary and might be anything; it depends on the server. Once you have the server up and running, you can open a browser to the address the site opened. Usually, something like http://localhost:3000. The number is arbitrary and might be anything; it depends on the server.

13
tutorial-styling.md Normal file
View File

@ -0,0 +1,13 @@
# Styling Tickle
Tickle's CSS is kept very simple on purpose. It matches elements, rather than classes, because that's less clutter.
If you just want to change the color scheme and fonts, you have css variables in the `:root` selector, which you can use for that.
If you want to add more styles, or edit styles, be aware that:
1. Modalities of the page are handled through toggling classes on the `body` element. Those modalities are:
1. `is-loading`: for when the loading overlay is shown
2. `menu-is-open`: for when the menu and overlay are open
3. `is-source-enabled`: for when the markdown source is shown
2. The navigation burger button *becomes* the open menu overlay. That saves an element and is neat, but it means that if you have transitions on your button, you'll get strange flashes as the button stretches and cover the entire page.