Compare commits

...

52 Commits
1.0 ... master

Author SHA1 Message Date
Lucien Cartier-Tilet b6e9ebf100
[Dart] Bump localStorage timestamp 2021-03-30 20:41:46 +02:00
Lucien Cartier-Tilet d645e29ce5
[SCSS, some dart] Theme rework, dark dropped, nord replaces dark
The old dark mode is dropped and replaced with nord. Light and black
themes were also rebased on the Nord theme.

Code blocks now appear the same way my terminal windows appear in my
Linux configuration, emulating a side titlebar.
2021-03-30 19:26:30 +02:00
Lucien Cartier-Tilet 291fc9111b
[dart] Upgrade to Dart 2.12 and its null-safety features 2021-03-30 18:54:31 +02:00
Lucien Cartier-Tilet 0e8e894056
[Style] add twitter card styling 2021-02-12 11:39:54 +01:00
Lucien Cartier-Tilet add728eb6d
[Style] Better titles display 2021-01-28 19:49:46 +01:00
Lucien Cartier-Tilet 6e30e3f361
Add before content for fish and rust source blocks 2021-01-02 14:18:45 +01:00
Lucien Cartier-Tilet f9e0ffa0eb
Add explicit size to header image, bump last update date 2020-12-21 20:38:24 +01:00
Lucien Cartier-Tilet 9632b008ac
Replace header image extension with webp 2020-12-21 20:29:25 +01:00
Lucien Cartier-Tilet 4cd74fd9b4
[Style] Make both SVGs and images centered 2020-12-21 16:05:28 +01:00
Lucien Cartier-Tilet 5355576443
Handle SVGs like images 2020-12-07 16:03:33 +01:00
Lucien Cartier-Tilet 975b5d3fb3
Make build dependencies not fixed to a specific version 2020-12-07 16:03:15 +01:00
Lucien Cartier-Tilet f0847a622f
Fix theme for pre tags with example class 2020-12-06 01:20:42 +01:00
Lucien Cartier-Tilet e69b9fb9c8
Image in header now round 2020-12-06 01:17:29 +01:00
Lucien Cartier-Tilet 16f23c08c6
[Dart] Cleaner code, force the web page to reload on modifier code
The code in `navbar.dart' is now more declarative and cleaner.

If the `lastUpdate' variable’s content change, then it will force the
user to reload the page, bypassing their cache. This can be useful in
case of an update with which the cache could break the website.
2020-08-28 22:23:35 +02:00
Lucien Cartier-Tilet e710c594f1
[Style] Add Nord theme, break up SCSS code
This commit adds the Nord theme into the list of available themes. The
Nord theme is now the default theme for dark-based systems.

Theme buttons now have their own class to make it easier to change
them without too much work.

The `style.scss' file has been split up in three different files:
- main.scss, the new file to load from the HTML page, imports the two
    other files
- themes.scss, containing all the theming of the websites
- style.scss, containing the rest

All websites loading the theme will now need to load it from
`main.scss' instead of `style.scss', likewise with the `.css'
extension
2020-08-28 22:19:42 +02:00
Lucien Cartier-Tilet e4dc101346
fix for dropdown pages 2020-08-28 17:13:31 +02:00
Lucien Cartier-Tilet f99d45e8ab
Simplify code, remove html dependency
This commit removes the `html' package dependency, speeding up the
compile time of the Dart code.

It also simplifies the code and removes some unused code. For instance
the sun, moon, and lightbulb icons is removed, the `Theme' class is
removed, and the `switchTheme' function is now merged into the
`setTheme' function. The `makeThemeItem' function has also had its
second argument removed.
2020-08-25 03:34:55 +02:00
Lucien Cartier-Tilet 8b984e301f
Fix #1
Also make website more mobile-friendly for smaller devices. And ignore
temporary scssc files from cache SCSS compiling.
2020-07-27 00:18:42 +02:00
Lucien Cartier-Tilet 272bd93ed8
Finally fix issue #9 2020-07-26 21:22:06 +02:00
Lucien Cartier-Tilet 167cad1ce3
Fix some errors with webpages nested in directories 2020-07-26 17:36:45 +02:00
Lucien Cartier-Tilet 71f38b13e9
Added correct path in README 2020-06-23 11:16:54 +02:00
Lucien Cartier-Tilet 1331fdaf79
Removed Docker files 2020-06-23 11:15:11 +02:00
Lucien Cartier-Tilet bffbd0c873
Updated README 2020-06-23 11:14:53 +02:00
Lucien Cartier-Tilet 659e406c5e
Removed some leftover from previous version 2020-06-23 11:14:36 +02:00
Lucien Cartier-Tilet fe05e145ab
Underline of tooltiped text is now more visible 2020-05-26 20:54:37 +02:00
Lucien Cartier-Tilet 94ab71402d
Fixed position issue with tooltips 2020-05-26 20:42:18 +02:00
Lucien Cartier-Tilet b804764e24
Fixed subtitle selection 2020-05-26 02:14:13 +02:00
Lucien Cartier-Tilet 4ed2d6bdc4
Fixed broken sitemap parsing 2020-05-26 02:13:48 +02:00
Lucien Cartier-Tilet 2334518ecc
Removed unused code 2020-05-26 02:13:28 +02:00
Lucien Cartier-Tilet 26b198d485
Code style 2020-05-26 00:52:05 +02:00
Lucien Cartier-Tilet fcfd8330ca
Smoother scroll behavior 2020-05-26 00:36:15 +02:00
Lucien Cartier-Tilet 82d3e3d018
Simpler code, removed a loop, code style 2020-05-26 00:35:55 +02:00
Lucien Cartier-Tilet 06994305ac
Code style, updated code for better icon handling 2020-05-26 00:35:34 +02:00
Lucien Cartier-Tilet 99c2127cc8
Fixes #7
Fontawesome was removed from the source code and is a dependency no
longer needed by websites running with OWB
2020-05-22 01:51:55 +02:00
Lucien Cartier-Tilet 08b8116f6f
Header image now easier to change, no need to modify source code 2020-05-21 08:29:59 +02:00
Lucien Cartier-Tilet 9725e24b38
Fixes #8
The default theme is now chosen based on the user’s preference stored in
their device. If the device uses a dark theme, the website’s dark theme
will be used by default. Otherwise, the light theme will be used by
default.
2020-05-21 07:34:10 +02:00
Lucien Cartier-Tilet d69682ec06
Fixed issue where sitemap wouldn’t load 2020-05-21 07:33:22 +02:00
Lucien Cartier-Tilet c9d263b12c
Simplified file, removed unused import 2020-05-21 07:32:47 +02:00
Lucien Cartier-Tilet 8ca55cb710
Better coding style 2020-05-10 12:37:22 +02:00
Lucien Cartier-Tilet aa4600b588
Sitemap query now fully asynchronous
Sitemap generation is now independent from the navbar generation, and
it will be added to the navbar only once the navbar has been created.

Also, better style for code.
2020-05-10 12:00:47 +02:00
Lucien Cartier-Tilet 2f297b6374
Updated minimum version of the Dart SDK required 2020-05-10 11:59:10 +02:00
Lucien Cartier-Tilet 9f6a32f5d2
Added build directory to gitignore 2020-05-10 11:58:51 +02:00
Lucien Cartier-Tilet 34ac1480d3
Upgraded Dart version, better Dockerfile and renamed project
Dart is now upgraded to version 2.8

Any change in dependencies or Dart tooling won’t trigger Docker to
reinstall anything Ruby-related in the image afterward

Name of the Dart package and its homepage were changed, forgot to do
that
2020-05-09 17:45:46 +02:00
Lucien Cartier-Tilet fc1556128c
Fixes #4
Also put in common some code between TOC and Pages dropdown, made style
more mobile-friendly.
2020-05-09 15:16:42 +02:00
Lucien Cartier-Tilet 3c942b4b8f
Fixes bug introduced in e819866
Table of content was made unavailable regardless of whether one was
provided by the page or not. This commit fixes this issue by making an
unavailable TOC only if one is not provided by the web page, otherwise
the TOC will be available.
2020-05-09 14:45:26 +02:00
Lucien Cartier-Tilet 90106df0f6
Added debug mode for backend 2020-05-07 15:46:36 +02:00
Lucien Cartier-Tilet a82fa74a6b
Fixes #2
Horizontal margin for content was reduced in order to better fit mobile
devices
2020-05-07 15:39:06 +02:00
Lucien Cartier-Tilet e81986683c
Fixes issue #5
The absence of a table of contents made the Dart code crash. This commit
adds a default TOC if non already exists.
2020-05-07 15:36:50 +02:00
Lucien Cartier-Tilet cc6519c302
Link best practices, and removed font for faster loading time 2020-05-06 11:48:51 +02:00
Lucien Cartier-Tilet 705e35e971
Removed some fonts from styling 2020-05-05 22:46:12 +02:00
Lucien Cartier-Tilet 61fe6f71a4
modified from final to const as it won’t change value 2020-05-05 19:33:57 +02:00
Lucien Cartier-Tilet 404e79211e
Made it easier to change header image 2020-05-05 19:20:28 +02:00
14 changed files with 672 additions and 583 deletions

3
.gitignore vendored
View File

@ -4,3 +4,6 @@
/.packages
/pubspec.lock
/.sass-cache/
/build/
*.scssc
/Book.html

View File

@ -1,20 +0,0 @@
FROM google/dart:2.7
WORKDIR /app
# Get Dart dependencies
RUN mkdir -p /pub-cache
ENV PUB_CACHE=/pub-cache
ENV PATH="${PATH}:/pub-cache/bin"
RUN pub global activate webdev
ADD pubspec.* /app/
RUN pub get
RUN pub get --offline
# Get Ruby Sass
RUN apt update && apt install ruby-sass ruby-dev build-essential -y
RUN gem install sass-listen
ADD . /app/
CMD ["./start.sh"]

View File

@ -46,65 +46,76 @@
that is also simple: it is the only one that provides a ~--watch~ option so
it automatically recompiles SCSS code to CSS when the SCSS code is changed.
** How to run this backend
** How to run this project
This backend delivers only two main files:
- =/dart/main.dart.js= The main dart file compiled to Javascript (you dont
need to worry about the others),
- =/style/style.css= The main style file compiled to CSS.
This is everything you need for beautiful org-generated websites.
*** Running locally
You could install Dart on your machine, as well as the Ruby implementation
of SASS with its dependencies. Next, you will need to install ~webdev~ and
install the Dart dependencies:
#+BEGIN_SRC sh
$ pub global activate webdev
$ pub get
#+END_SRC
While I tried to run this inside a Docker environment serving the Dart and
CSS files on a HTTP port to which I tried to redirect requests, I found the
easiest way was to actually compile everything into a ~build~ directory and
to simlink its content to the root directory of your org website like so:
#+BEGIN_SRC sh
cd /path/to/your/org/website
ln -s /path/to/your/owb/directory/build/*
#+END_SRC
By the way, you have to ensure your Dart caches bins are in your ~$PATH~.
They are generally installed in your ~$HOME/.pub-cache/bin~ directory.
Here is what I add at the top of my org files in order to get it working:
#+BEGIN_SRC org
,#+HTML_HEAD_EXTRA: <link rel="stylesheet" href="/style/style.css"/>
,#+HTML_HEAD_EXTRA: <script defer src="/dart/main.dart.js"></script>
#+END_SRC
In this order, it will make your website display stuff a bit faster.
Then, you have to run ~start.sh~, and youre good to go! Content will be
delivered on the 8080 port. If you wish to deliver content to another port,
you can edit this file.
#+BEGIN_SRC sh
./start.sh
#+END_SRC
To compile the project, you will need to install Dart on your machine as well
as a SCSS compiler. While the latter is up to you, make sur the result CSS
files are in the same directory as their original SCSS counterpart. As for
the Dart part of the project, you can run the following to create a release
build of the project:
#+BEGIN_SRC sh
pub global activate webdev
webdev build -r
#+END_SRC
*** Docker
A Dockerfile is also provided so you can run this server inside a Docker
container, and thus you can avoid the hassle of installing Dart and Ruby
Sass. In order to run OWB, you can first build the Docker image:
#+BEGIN_SRC sh
docker build . --tag owb:1.0
#+END_SRC
If ~pub~ is not found as an executable, make sure ~$HOME/.pub-cache/bin~ is
in your ~$PATH~. Same goes for ~webdev~ even after you install it.
And then you can run it:
** Running in development mode
# To run this backend in development mode, you will have to remove the
# ~--release~ option from the ~webdev~ command in the ~start.sh~ file. This
# will allow webdev to compile Dart files faster, but at the price of slower
# compiled Javascript files. If you use Docker, dont forget to rebuild your
# image.
To run this backend in development mode, you can add to your environment the
variable ~RELEASE~ with the value ~debug~. Running the backend locally, you
would start it like so:
#+BEGIN_SRC sh
RELEASE=debug ./start.sh
#+END_SRC
Running it with Docker, you would use the following command:
#+BEGIN_SRC sh
docker run \
-p 8080:8080 \
-v ./web:/app/web \
-e RELEASE=debug \
--restart always \
--detach \
--name owb \
owb:1.0
#+END_SRC
*** Docker-compose
This repository also provides a ~docker-compose.yml~ file for easier Docker
usage with ~docker-compose~. If you wish to run your backend in
release-mode, simply run the following:
#+BEGIN_SRC sh
docker-compose up --detach
And with docker-compose, you would add the following line to your ~owb~
service:
#+BEGIN_SRC yaml
environment:
- RELEASE=debug
#+END_SRC
** Running in development mode
To run this backend in development mode, you will have to remove the
~--release~ option from the ~webdev~ command in the ~start.sh~ file. This
will allow webdev to compile Dart files faster, but at the price of slower
compiled Javascript files. If you use Docker, dont forget to rebuild your
image.
Any other value to this environment variable will make your backend run in
release mode (actually, it will only make ~webdev~ run in release mode).
** How can I use this in my org files?
Lets say you serve your files on org.example.com, add the following lines to

View File

@ -1,10 +0,0 @@
version: '3'
services:
owb:
build: .
ports:
- 8010:8080
restart: always
volumes:
- ./web:/app/web

View File

@ -1,16 +1,15 @@
name: languephundrakcom
description: A bare-bone server for my linguistics website.
name: orgwebsitebackend
description: A bare-bone server for org-generated websites.
version: 1.0.0
homepage: https://langue.phundrak.com
homepage: https://labs.phundrak.com/phundrak/org-website-backend
author: Lucien Cartier-Tilet <lucien@phundrak.com>
environment:
sdk: '>=2.5.0 <3.0.0'
sdk: '>=2.12.0 <3.0.0'
dependencies:
html: '^0.14.0+3'
dev_dependencies:
build_runner: ^1.8.0
build_web_compilers: ^2.9.0
pedantic: ^1.9.0
build_runner: ">=1.10.1 <2.0.0"
build_web_compilers: ">=2.9.0 <3.0.0"
pedantic: ">=1.9.0 <2.0.0"

View File

@ -1,3 +1,8 @@
#!/bin/bash
sass --watch web/style/:web/style -tcompressed &
webdev serve --release --hostname 0.0.0.0
[ "$RELEASE" == "debug" ] \
&& webdev serve --hostname 0.0.0.0 \
|| webdev serve --release --hostname 0.0.0.0
# webdev serve --release --hostname 0.0.0.0

View File

@ -1,3 +1,4 @@
import './parse_sitemap.dart' show createSitemap;
import './reorganize_html.dart' show reorganizeHtml;
import './theme.dart' show enableThemeChanger, setTheme;
@ -5,5 +6,6 @@ Future<void> main() async {
await setTheme();
await reorganizeHtml().then((_) {
enableThemeChanger();
createSitemap();
});
}

View File

@ -1,117 +1,43 @@
import 'dart:html';
import 'dart:html' show querySelector, Element, window;
import 'dart:svg' show SvgElement;
import './parse_sitemap.dart' show parseSitemap;
// You will see here and there some 'tabindex' attributes added to various HTML
// elements, and Im sure you will ask "Why? They dont serve any purpose, do
// they?". Well you are wrong. Webkit has a **terrible** implementation of
// `:focus-within`, and the dropdowns will **not** work unless the parent
// element, in this case a `<li>` has a `tabindex` attribute set to `0` and its
// first child set to `-1`.
//
// Screw WebKit, and screw Apple for using such a terrible web engine.
// Returns the title of the current webpage
String getPageTitle() {
return querySelector('title').text;
}
// Icons from https://materialdesignicons.com/
final icons = {
'home': SvgElement.svg(
'<svg style="width:24px;height:24px" viewBox="0 0 24 24"><path fill="currentColor" d="M10,20V14H14V20H19V12H22L12,3L2,12H5V20H10Z" /></svg>'),
'pages': SvgElement.svg(
'<svg style="width:24px;height:24px" viewBox="0 0 24 24"><path fill="currentColor" d="M19,2L14,6.5V17.5L19,13V2M6.5,5C4.55,5 2.45,5.4 1,6.5V21.16C1,21.41 1.25,21.66 1.5,21.66C1.6,21.66 1.65,21.59 1.75,21.59C3.1,20.94 5.05,20.5 6.5,20.5C8.45,20.5 10.55,20.9 12,22C13.35,21.15 15.8,20.5 17.5,20.5C19.15,20.5 20.85,20.81 22.25,21.56C22.35,21.61 22.4,21.59 22.5,21.59C22.75,21.59 23,21.34 23,21.09V6.5C22.4,6.05 21.75,5.75 21,5.5V7.5L21,13V19C19.9,18.65 18.7,18.5 17.5,18.5C15.8,18.5 13.35,19.15 12,20V13L12,8.5V6.5C10.55,5.4 8.45,5 6.5,5V5Z" /></svg>'),
'toc': SvgElement.svg(
'<svg style="width:24px;height:24px" viewBox="0 0 24 24"><path fill="currentColor" d="M3,4H7V8H3V4M9,5V7H21V5H9M3,10H7V14H3V10M9,11V13H21V11H9M3,16H7V20H3V16M9,17V19H21V17H9"/></svg>'),
'share': SvgElement.svg(
'<svg style="width:24px;height:24px" viewBox="0 0 24 24"><path fill="currentColor" d="M18,16.08C17.24,16.08 16.56,16.38 16.04,16.85L8.91,12.7C8.96,12.47 9,12.24 9,12C9,11.76 8.96,11.53 8.91,11.3L15.96,7.19C16.5,7.69 17.21,8 18,8A3,3 0 0,0 21,5A3,3 0 0,0 18,2A3,3 0 0,0 15,5C15,5.24 15.04,5.47 15.09,5.7L8.04,9.81C7.5,9.31 6.79,9 6,9A3,3 0 0,0 3,12A3,3 0 0,0 6,15C6.79,15 7.5,14.69 8.04,14.19L15.16,18.34C15.11,18.55 15.08,18.77 15.08,19C15.08,20.61 16.39,21.91 18,21.91C19.61,21.91 20.92,20.61 20.92,19A2.92,2.92 0 0,0 18,16.08Z" /></svg>'),
'twitter': SvgElement.svg(
'<svg style="width:24px;height:24px" viewBox="0 0 24 24"><path fill="currentColor" d="M22.46,6C21.69,6.35 20.86,6.58 20,6.69C20.88,6.16 21.56,5.32 21.88,4.31C21.05,4.81 20.13,5.16 19.16,5.36C18.37,4.5 17.26,4 16,4C13.65,4 11.73,5.92 11.73,8.29C11.73,8.63 11.77,8.96 11.84,9.27C8.28,9.09 5.11,7.38 3,4.79C2.63,5.42 2.42,6.16 2.42,6.94C2.42,8.43 3.17,9.75 4.33,10.5C3.62,10.5 2.96,10.3 2.38,10C2.38,10 2.38,10 2.38,10.03C2.38,12.11 3.86,13.85 5.82,14.24C5.46,14.34 5.08,14.39 4.69,14.39C4.42,14.39 4.15,14.36 3.89,14.31C4.43,16 6,17.26 7.89,17.29C6.43,18.45 4.58,19.13 2.56,19.13C2.22,19.13 1.88,19.11 1.54,19.07C3.44,20.29 5.7,21 8.12,21C16,21 20.33,14.46 20.33,8.79C20.33,8.6 20.33,8.42 20.32,8.23C21.16,7.63 21.88,6.87 22.46,6Z" /></svg>'),
'reddit': SvgElement.svg(
'<svg style="width:24px;height:24px" viewBox="0 0 24 24"><path fill="currentColor" d="M14.5 15.41C14.58 15.5 14.58 15.69 14.5 15.8C13.77 16.5 12.41 16.56 12 16.56C11.61 16.56 10.25 16.5 9.54 15.8C9.44 15.69 9.44 15.5 9.54 15.41C9.65 15.31 9.82 15.31 9.92 15.41C10.38 15.87 11.33 16 12 16C12.69 16 13.66 15.87 14.1 15.41C14.21 15.31 14.38 15.31 14.5 15.41M10.75 13.04C10.75 12.47 10.28 12 9.71 12C9.14 12 8.67 12.47 8.67 13.04C8.67 13.61 9.14 14.09 9.71 14.08C10.28 14.08 10.75 13.61 10.75 13.04M14.29 12C13.72 12 13.25 12.5 13.25 13.05S13.72 14.09 14.29 14.09C14.86 14.09 15.33 13.61 15.33 13.05C15.33 12.5 14.86 12 14.29 12M22 12C22 17.5 17.5 22 12 22S2 17.5 2 12C2 6.5 6.5 2 12 2S22 6.5 22 12M18.67 12C18.67 11.19 18 10.54 17.22 10.54C16.82 10.54 16.46 10.7 16.2 10.95C15.2 10.23 13.83 9.77 12.3 9.71L12.97 6.58L15.14 7.05C15.16 7.6 15.62 8.04 16.18 8.04C16.75 8.04 17.22 7.57 17.22 7C17.22 6.43 16.75 5.96 16.18 5.96C15.77 5.96 15.41 6.2 15.25 6.55L12.82 6.03C12.75 6 12.68 6.03 12.63 6.07C12.57 6.11 12.54 6.17 12.53 6.24L11.79 9.72C10.24 9.77 8.84 10.23 7.82 10.96C7.56 10.71 7.2 10.56 6.81 10.56C6 10.56 5.35 11.21 5.35 12C5.35 12.61 5.71 13.11 6.21 13.34C6.19 13.5 6.18 13.62 6.18 13.78C6.18 16 8.79 17.85 12 17.85C15.23 17.85 17.85 16.03 17.85 13.78C17.85 13.64 17.84 13.5 17.81 13.34C18.31 13.11 18.67 12.6 18.67 12Z" /></svg>'),
'email': SvgElement.svg(
'<svg style="width:24px;height:24px" viewBox="0 0 24 24"><path fill="currentColor" d="M20,8L12,13L4,8V6L12,11L20,6M20,4H4C2.89,4 2,4.89 2,6V18A2,2 0 0,0 4,20H20A2,2 0 0,0 22,18V6C22,4.89 21.1,4 20,4Z" /></svg>'),
'linkedin': SvgElement.svg(
'<svg style="width:24px;height:24px" viewBox="0 0 24 24"><path fill="currentColor" d="M19 3A2 2 0 0 1 21 5V19A2 2 0 0 1 19 21H5A2 2 0 0 1 3 19V5A2 2 0 0 1 5 3H19M18.5 18.5V13.2A3.26 3.26 0 0 0 15.24 9.94C14.39 9.94 13.4 10.46 12.92 11.24V10.13H10.13V18.5H12.92V13.57C12.92 12.8 13.54 12.17 14.31 12.17A1.4 1.4 0 0 1 15.71 13.57V18.5H18.5M6.88 8.56A1.68 1.68 0 0 0 8.56 6.88C8.56 5.95 7.81 5.19 6.88 5.19A1.69 1.69 0 0 0 5.19 6.88C5.19 7.81 5.95 8.56 6.88 8.56M8.27 18.5V10.13H5.5V18.5H8.27Z" /></svg>'),
'facebook': SvgElement.svg(
'<svg style="width:24px;height:24px" viewBox="0 0 24 24"><path fill="currentColor" d="M12 2.04C6.5 2.04 2 6.53 2 12.06C2 17.06 5.66 21.21 10.44 21.96V14.96H7.9V12.06H10.44V9.85C10.44 7.34 11.93 5.96 14.22 5.96C15.31 5.96 16.45 6.15 16.45 6.15V8.62H15.19C13.95 8.62 13.56 9.39 13.56 10.18V12.06H16.34L15.89 14.96H13.56V21.96A10 10 0 0 0 22 12.06C22 6.53 17.5 2.04 12 2.04Z" /></svg>'),
'theme': SvgElement.svg(
'<svg style="width:24px;height:24px" viewBox="0 0 24 24"><path fill="currentColor" d="M12,18V6A6,6 0 0,1 18,12A6,6 0 0,1 12,18M20,15.31L23.31,12L20,8.69V4H15.31L12,0.69L8.69,4H4V8.69L0.69,12L4,15.31V20H8.69L12,23.31L15.31,20H20V15.31Z" /></svg>'),
};
Element makeIcon(List<String> classes, [String id]) {
final icon = Element.tag('i')..classes.addAll(classes);
if (id != null) {
icon.attributes['id'] = id;
}
return icon;
}
Future<Element> makeToc() async {
return Element.li()
..attributes['id'] = 'toc-drop'
..classes.addAll(['nav-item', 'has-dropdown'])
..append(Element.a()
..attributes['href'] = 'javascript:void(0)'
..append(makeIcon(['fas', 'fa-list-ol'], 'tocBtn')));
}
Future<Element> makePages() async {
var pages = Element.ul()
..attributes['id'] = 'drop-page'
..classes.add('dropdown');
await parseSitemap().then((final sitemap) => {
sitemap.forEach((url, name) {
final link = Element.li()
..classes.add('dropdown-item')
..insertAdjacentElement(
'afterBegin',
Element.a()
..attributes['href'] = url
..innerText = name);
pages.insertAdjacentElement('beforeEnd', link);
})
});
return Element.li()
..append(Element.a()
..attributes['href'] = 'javascript:void(0)'
..append(makeIcon(['fas', 'fa-flag'])))
..classes.addAll(['nav-item', 'has-dropdown'])
..insertAdjacentElement('beforeEnd', pages);
}
Element makeShareLink(Element icon, String url) {
return Element.li()
..classes.add('dropdown-item')
..append(Element.a()
..attributes['href'] = url
..attributes['target'] = '_blank'
..append(icon));
}
Future<Element> makeShare() async {
return Element.li()
..classes.addAll(['nav-item', 'has-dropdown'])
..append(Element.a()
..attributes['href'] = 'javascript:void(0)'
..append(makeIcon(['fas', 'fa-share-alt'])))
..append(Element.ul()
..classes.add('dropdown')
..attributes['id'] = 'drop-share'
..append(makeShareLink(
makeIcon(['fab', 'fa-twitter-square']),
'https://twitter.com/share?text=${getPageTitle()}'
'&url=${window.location.href}'))
..append(makeShareLink(makeIcon(['fab', 'fa-reddit-square']),
'https://www.reddit.com/submit?title=${getPageTitle()}s&url=${window.location.href}'))
..append(makeShareLink(makeIcon(['fas', 'fa-envelope-square']),
'mailto:?subject=${getPageTitle}&body=${window.location.href}'))
..append(makeShareLink(
makeIcon(['fab', 'fa-linkedin']),
'https://www.linkedin.com/shareArticle?mini=true&url=${window.location.href}'
'&title=${getPageTitle()}'))
..append(makeShareLink(makeIcon(['fab', 'fa-facebook-square']),
'https://www.facebook.com/sharer/sharer.php?u=${window.location.href}')));
}
Future<Element> makeThemeChanger() async {
Element makeThemeItem(String t_btnId, Element t_icon, String t_text) {
return Element.li()
..classes.add('dropdown-item')
..append(Element.span()
..attributes['id'] = t_btnId
..append(t_icon)
..appendText(' $t_text'));
}
return Element.li()
..classes.addAll(['nav-item', 'has-dropdown'])
..append(Element.a()
..attributes['href'] = 'javascript:void(0)'
..append(Element.span()
..classes.add('fa-stack')
..style.verticalAlign = 'top'
..append(makeIcon(['fas', 'fa-sun', 'fa-stack-1x'])
..style.fontSize = '0.9em')
..append(makeIcon(['fas', 'fa-moon', 'fa-stack-1x']))))
..append(Element.ul()
..classes.add('dropdown')
..attributes['id'] = 'theme-dropdown'
..append(makeThemeItem('lightBtn', makeIcon(['fas', 'fa-sun']), 'Clair'))
..append(
makeThemeItem('darkBtn', makeIcon(['fas', 'fa-lightbulb']), 'Sombre'))
..append(
makeThemeItem('blackBtn', makeIcon(['fas', 'fa-moon']), 'Noir')));
}
// Get the current pages title
String getPageTitle() => querySelector('title')!.text!;
// Create the home button
Future<Element> makeHome() async {
return Element.li()
..classes.add('nav-item')
@ -119,19 +45,120 @@ Future<Element> makeHome() async {
'afterBegin',
Element.a()
..attributes['href'] = '/'
..append(makeIcon(['fas', 'fa-home'])));
..append(makeIcon(icons['home']!)));
}
// Add a navbar atop of the HTML body, containing two buttons:
// - One back to home
// - A dropdown to each page detected in the sitemap
// Create a clickable icon
// `t_elem` must be an SVG declared in the above `icons` variable.
Element makeIcon(SvgElement t_elem) {
return t_elem..classes.add('nav-icon');
}
// Create the dropdown sitemap
Future<Element> makePages() async {
return Element.li()
..attributes['tabindex'] = '0'
..append(Element.a()
..attributes['tabindex'] = '-1'
..attributes['href'] = 'javascript:void(0)'
..append(makeIcon(icons['pages']!)))
..classes.addAll(['nav-item', 'has-dropdown'])
..append(Element.ul()
..attributes['id'] = 'drop-page'
..classes.add('dropdown'));
}
// Create the array of share icons
Future<Element> makeShare() async {
// Create a share button
Element makeShareLink(Element t_icon, String t_url) {
return Element.li()
..classes.add('dropdown-item')
..append(Element.a()
..attributes['href'] = t_url
..attributes['target'] = '_blank'
..attributes['rel'] = 'noreferrer'
..append(t_icon));
}
return Element.li()
..classes.addAll(['nav-item', 'has-dropdown'])
..attributes['tabindex'] = '0'
..append(Element.a()
..attributes['href'] = 'javascript:void(0)'
..attributes['tabindex'] = '-1'
..append(makeIcon(icons['share']!)))
..append(Element.ul()
..classes.add('dropdown')
..attributes['id'] = 'drop-share'
..append(makeShareLink(
makeIcon(icons['twitter']!),
'https://twitter.com/share?text=${getPageTitle()}'
'&url=${window.location.href}'))
..append(makeShareLink(makeIcon(icons['reddit']!),
'https://www.reddit.com/submit?title=${getPageTitle()}s&url=${window.location.href}'))
..append(makeShareLink(makeIcon(icons['email']!),
'mailto:?subject=$getPageTitle&body=${window.location.href}'))
..append(makeShareLink(
makeIcon(icons['linkedin']!),
'https://www.linkedin.com/shareArticle?mini=true&url=${window.location.href}'
'&title=${getPageTitle()}'))
..append(makeShareLink(makeIcon(icons['facebook']!),
'https://www.facebook.com/sharer/sharer.php?u=${window.location.href}')));
}
// Create the theme changer
Future<Element> makeThemeChanger() async {
Element makeThemeItem(String t_btnId) {
return Element.li()
..classes.add('dropdown-item')
..append(Element.span()
..attributes['id'] = t_btnId
..classes.add('themeBtn'));
}
return Element.li()
..classes.addAll(['nav-item', 'has-dropdown'])
..attributes['tabindex'] = '0'
..append(Element.a()
..attributes['href'] = 'javascript:void(0)'
..attributes['tabindex'] = '-1'
..append(Element.span()
..style.verticalAlign = 'top'
..append(makeIcon(icons['theme']!))))
..append(Element.ul()
..classes.add('dropdown')
..attributes['id'] = 'theme-dropdown'
..append(makeThemeItem('lightBtn'))
..append(makeThemeItem('darkBtn'))
..append(makeThemeItem('blackBtn')));
}
// Create the dropdown table of contents
Future<Element> makeToc() async {
return Element.li()
..attributes['id'] = 'toc-drop'
..attributes['tabindex'] = '0'
..classes.addAll(['nav-item', 'has-dropdown'])
..append(Element.a()
..attributes['tabindex'] = '-1'
..attributes['href'] = 'javascript:void(0)'
..append(makeIcon(icons['toc']!)));
}
// Add a navbar atop of the HTML body, containing:
// - A back to home button
// - A dropdown sitemap
// - A dropdown table of contents
// - A dropdown array of share icons
// - A theme changer
Future<Element> makeNavbar() async {
final navbar_content = Element.ul()..classes.add('navbar-nav');
final home = await makeHome();
final pages = await makePages();
final toc = await makeToc();
final share = await makeShare();
final theme = await makeThemeChanger();
final home = await makeHome();
final pages = await makePages();
final toc = await makeToc();
final share = await makeShare();
final theme = await makeThemeChanger();
navbar_content
..append(home)

View File

@ -1,13 +1,10 @@
import 'dart:html' show HttpRequest;
import 'dart:html' show HttpRequest, Element, querySelector;
import 'package:html/parser.dart' show parse;
import 'package:html/dom.dart' show Element;
final excluded_keywords = ['index', 'CONTRIBUTING', 'LICENSE', 'README'];
final excluded_keywords = {'index', 'CONTRIBUTING', 'LICENSE', 'README'};
// Get the sitemap content
Future<String> getSitemap() async {
const path = 'sitemap.html';
Future<String> fetchRemoteSitemap() async {
const path = '/sitemap.html';
try {
return await HttpRequest.getString(path);
} catch (e) {
@ -17,32 +14,50 @@ Future<String> getSitemap() async {
}
// Parse the list of elements and detect pages from this list
Map<String, String> detectPages(List<Element> sitemap, [String prefix]) {
final links = <String, String>{};
for (var elem in sitemap) {
for(var kw in excluded_keywords) {
if (elem.outerHtml.contains(kw)) {
Map<String, String> detectPages(List<Element> t_sitemap,
[String? t_prefix, Map<String, String>? t_links]) {
t_links ??= <String, String>{};
// parse each element in sitemap
for (var elem in t_sitemap) {
// if its a link
if (elem.innerHtml!.startsWith('<a')) {
elem = elem.children[0];
final url = elem.attributes['href']!;
final text = elem.children[0].text;
if (excluded_keywords.contains(text) ||
excluded_keywords.contains(url.substring(0, url.length - 5))) {
continue;
}
}
if (elem.innerHtml.startsWith('<a')) {
elem = elem.firstChild;
final url = elem.attributes['href'];
final text = elem.firstChild.text;
links[url] = (prefix == null) ? text : '$text ($prefix)';
t_links!['/$url'] = ((t_prefix == null) ? text : '$text ($t_prefix)')!;
} else {
final prefix = elem.firstChild.text;
final ul = elem.children[0].children;
links.addAll(detectPages(ul, prefix));
final prefix = (t_prefix == null)
? elem.firstChild!.text!.replaceAll('\n', '')
: '$t_prefix / ${elem.firstChild!.text}'.replaceAll('\n', '');
t_links = detectPages(elem.children[0].children, prefix, t_links);
}
}
return links;
return t_links!;
}
// This function returns a Map which contains all links to languages detected
// from the sitemap.
Future<Map<String, String>> parseSitemap() async {
final content = await getSitemap();
final sitemap = parse(content).getElementsByClassName('org-ul')[0].children;
return detectPages(sitemap);
final content = await fetchRemoteSitemap();
final sitemap = Element.ul()..innerHtml = content;
final sitemapNodes = sitemap.querySelector('ul')!.children;
return detectPages(sitemapNodes);
}
Future<void> createSitemap() async {
final sitemap = await parseSitemap();
final pages = querySelector('#drop-page')!;
sitemap.forEach((url, name) {
final link = Element.li()
..classes.add('dropdown-item')
..append(Element.a()
..attributes['href'] = url
..innerText = name);
pages.append(link);
});
}

View File

@ -1,18 +1,30 @@
import 'dart:html';
import 'dart:html'
show DivElement, Element, querySelector, querySelectorAll, window;
import './navbar.dart' show makeNavbar;
const image_header = '/img/icon.webp';
Future<void> makeDecorativeButtonsOrgSrc() async {
for (var pre in querySelectorAll('.org-src-container')) {
pre
..append(Element.div()..attributes['class'] = 'closeButton')
..append(Element.div()..attributes['class'] = 'minButton')
..append(Element.div()..attributes['class'] = 'maxButton')
..append(Element.div()..attributes['class'] = 'floatButton');
}
}
Future<Element> makeHeader() async {
var header = Element.tag('header');
header
..append(Element.img()
..attributes['src'] =
'https://cdn.phundrak.com/img/mahakala-monochrome.png'
..attributes['alt'] = 'Logo de Phundrak'
..attributes['heigh'] = '150px'
..attributes['src'] = image_header
..attributes['alt'] = 'Logo'
..attributes['height'] = '150px'
..attributes['width'] = '150px')
..append(querySelector('h1'));
var subt = querySelector('.subtitle');
..append(querySelector('h1')!);
final subt = header.querySelector('.subtitle');
if (subt != null) {
header.append(subt);
}
@ -28,12 +40,10 @@ Future<void> wrapTables() async {
}
// All images that are not nested inside a link will be linkified to themselves.
void linkifyImg() {
Future<void> linkifyImg() async {
querySelectorAll('img').forEach((img) {
print(img.attributes['src']);
print(img.parent.tagName);
if (img.parent.tagName == 'P') {
final link = Element.a()..attributes['href'] = img.attributes['src'];
if (img.parent!.tagName == 'P') {
final link = Element.a()..attributes['href'] = img.attributes['src']!;
img.insertAdjacentElement('beforeBegin', link);
link.append(img);
}
@ -41,30 +51,41 @@ void linkifyImg() {
}
Future<void> reorganizeHtml() async {
final content = querySelector('#content');
final ls = window.localStorage;
final lastUpdate = '20210330';
if (ls['last-update'] != lastUpdate) {
ls['last-update'] = lastUpdate;
window.location.reload();
}
// Make navbar
await makeNavbar().then((navbar) {
querySelector('body').insertAdjacentElement('afterBegin', navbar);
});
final navbar = await makeNavbar();
// Make header
await makeHeader().then((header) {
content.insertAdjacentElement('beforeBegin', header);
final subtitle = querySelector('.subtitle');
if (subtitle != null) {
header.append(subtitle);
}
});
final header = await makeHeader();
// Add decorative divs to source block wrappers
await makeDecorativeButtonsOrgSrc();
// wrap tables in container for better SCSS display
await wrapTables();
linkifyImg();
// Make images not linking somewhere link to themselves
await linkifyImg();
// Add navbar to page
querySelector('body')!.insertAdjacentElement('afterBegin', navbar);
// Add headet to page
querySelector('#content')!.insertAdjacentElement('beforeBegin', header);
// Add correct class to TOC
querySelector('#toc-drop')
.append(querySelector('#table-of-contents')..classes.add('dropdown'));
final toc = (querySelector('#table-of-contents') ??
(Element.div()
..attributes['id'] = 'table-of-contents'
..innerText = 'Table of Contents Unavailable'))
..classes.add('dropdown');
navbar.querySelector('#toc-drop')!.append(toc);
// Remove all <br> tags from HTML
querySelectorAll('br').forEach((br) => br.remove());

View File

@ -1,53 +1,20 @@
import 'dart:html';
class Theme {
String _name;
String _icon;
Theme(String t_name, String t_icon) {
_name = t_name;
_icon = t_icon;
}
String getIcon() => _icon;
String getName() => _name;
}
final themes = {
'light': Theme('light', 'fa-sun'),
'dark': Theme('dark', 'fa-lightbulb'),
'black': Theme('black', 'fa-moon')
};
import 'dart:html' show window, querySelector;
final localStorage = window.localStorage;
var currentTheme = themes[localStorage['theme']];
Future<void> setTheme([String? theme]) async {
theme ??= localStorage['theme'] ??
(window.matchMedia('(prefers-color-scheme: dark)').matches
? 'dark'
: 'light');
localStorage['theme'] = theme;
querySelector('body')!
..classes.clear()
..classes.add(theme);
}
void enableThemeChanger() {
final darkBtn = querySelector('#darkBtn');
final lightBtn = querySelector('#lightBtn');
final blackBtn = querySelector('#blackBtn');
lightBtn.onClick.listen((_) => switchTheme(themes['light']));
darkBtn.onClick.listen((_) => switchTheme(themes['dark']));
blackBtn.onClick.listen((_) => switchTheme(themes['black']));
}
Future<void> setTheme() async {
if (currentTheme == null) {
currentTheme = themes['light'];
localStorage['theme'] = currentTheme.getName();
}
querySelector('body')
..classes.clear()
..classes.add(currentTheme.getName());
}
void switchTheme(Theme currentTheme) {
// Set HTML theme
querySelector('body')
..classes.clear()
..classes.add(currentTheme.getName());
// Set storage theme
localStorage['theme'] = currentTheme.getName();
final themes = <String>['light', 'dark', 'black'];
themes.forEach((theme) =>
querySelector('#${theme}Btn')!.onClick.listen((_) => setTheme(theme)));
}

2
web/style/main.scss Normal file
View File

@ -0,0 +1,2 @@
@import "themes.scss";
@import "style.scss";

View File

@ -1,21 +1,4 @@
@import url(https://fonts.googleapis.com/css?family=Lato:400,700,400italic,700italic|Roboto+Slab:400,700|Inconsolata:400,700);
@font-face {
font-family: "DoulosSIL";
font-display: swap;
src: url("/fonts/DoulosSIL-R.woff");
}
@font-face {
font-family: "Noto Sans Runes";
font-display: swap;
src: url("../fonts/NotoSansRunic-Regular.ttf");
}
@font-face {
font-family: "Helvetica Neue";
font-display: swap;
src: url("../fonts/HelveticaNeue.ttf");
}
/* Variables *****************************************************************/
// Variables //////////////////////////////////////////////////////////////////
$switch-small-screen: "only screen and (max-width: 600px)";
$switch-smaller-screen: "only screen and (max-width: 400px)";
@ -23,261 +6,20 @@ $switch-smaller-screen: "only screen and (max-width: 400px)";
$navbar-height: 70px;
$postamble-height: 55px;
// Themes /////////////////////////////////////////////////////////////////////
$dark: rgba( 52, 73, 94, 1);
$black: rgba( 0, 0, 0, 1);
$accent1: rgba( 93, 115, 126, 1);
$accent2: rgba( 92, 172, 126, 1);
$accent3: rgba(197, 193, 155, 1);
$light: #eee;
$grey1: #f8f8f8;
$grey2: #dbe1e8;
$grey3: #b2becd;
$grey4: #6c7983;
$grey5: #454e56;
$grey6: #12181b;
// Accent 1
// Black
$gradient-accent1-black-left: linear-gradient(to left, $black, $accent1, $accent1);
$gradient-accent1-black-right: linear-gradient(to right, $black, $accent1, $accent1);
// Dark
$gradient-accent1-dark-left: linear-gradient(to left, $dark, $accent1);
$gradient-accent1-dark-right: linear-gradient(to right, $dark, $accent1);
// Light
$gradient-accent1-light-left: linear-gradient(to left, $light, $accent1);
$gradient-accent1-light-right: linear-gradient(to right, $light, $accent1);
// Accent 2
// Black
$gradient-accent2-black-left: linear-gradient(to left, $black, $accent2, $accent2);
$gradient-accent2-black-right: linear-gradient(to right, $black, $accent2, $accent2);
// Dark
$gradient-accent2-dark-left: linear-gradient(to left, $dark, $accent2);
$gradient-accent2-dark-right: linear-gradient(to right, $dark, $accent2);
// Light
$gradient-accent2-light-left: linear-gradient(to left, $light, $accent2);
$gradient-accent2-light-right: linear-gradient(to right, $light, $accent2);
// Accent 3
// Black
$gradient-accent3-black-left: linear-gradient(to left, $black, $accent3, $accent3);
$gradient-accent3-black-right: linear-gradient(to right, $black, $accent3, $accent3);
// Dark
$gradient-accent3-dark-left: linear-gradient(to left, $dark, $accent3);
$gradient-accent3-dark-right: linear-gradient(to right, $dark, $accent3);
// Light
$gradient-accent3-light-left: linear-gradient(to left, $light, $accent3);
$gradient-accent3-light-right: linear-gradient(to right, $light, $accent3);
.light {
$bg-nav: $gradient-accent3-light-right;
$border-color: $accent1;
color: $dark;
background: $light;
transition: background 500ms ease-in-out, color 1s ease-in-out;
pre {
box-shadow: 3px 3px $dark;
border-color: $light;
}
pre.src {
&::before {
background-color: $light;
color: $dark;
}
}
.navbar, header {
background: $bg-nav;
}
.status {
background: $gradient-accent3-light-left;
color: $dark;
}
.tooltip {
border-bottom: 1px dotted $accent3;
.tooltiptext {
background-color: $accent3;
color: $dark;
&::after {
border-color: $accent3 transparent transparent transparent;
}
}
}
.dropdown {
background: $accent3;
color: $dark;
}
#content {
a {
box-shadow: inset 0 -3px 0 $accent3;
transition: box-shadow 300ms ease-in-out;
&:hover {
box-shadow: inset 0 -23px 0 $accent3;
transition: box-shadow 300ms ease-in-out;
}
}
}
table, th, td {
border: 1px solid $dark;
}
th {
background: darken($light, 5%);
}
.gentree {
filter: invert(0%);
transition: filter 1s ease-in-out;
}
}
.dark, .black {
$bg-nav: $gradient-accent2-dark-right;
$border-color: $dark;
color: $light;
background: $dark;
transition: background 500ms ease-in-out, color 1s ease-in-out;
pre {
box-shadow: 3px 3px $dark;
border: none;
}
pre.src {
&::before {
background-color: $dark;
color: $light;
}
}
.navbar, header {
background: $bg-nav;
}
.status {
background: $gradient-accent2-dark-left;
color: $light;
}
.tooltip {
border-bottom: 1px dotted $accent1;
.tooltiptext {
background-color: $accent1;
color: $light;
&::after {
border-color: $accent1 transparent transparent transparent;
}
}
}
.dropdown {
background: $accent3;
color: $dark;
}
#content {
a {
box-shadow: inset 0 -3px 0 $accent2;
transition: box-shadow 300ms ease-in-out;
&:hover {
box-shadow: inset 0 -23px 0 $accent2;
transition: box-shadow 300ms ease-in-out;
}
}
}
table, th, td {
border: 1px solid $accent1;
}
th {
background: darken($dark, 2.5%);
}
.gentree {
filter: invert(100%);
transition: filter 1s ease-in-out;
}
}
.black {
$bg-nav: $gradient-accent1-black-right;
background: $black;
pre {
box-shadow: 3px 3px $light;
border: none;
}
pre.src {
&::before {
background-color: $black;
color: $light;
}
}
.navbar, header {
background: $bg-nav;
}
.status {
background: $gradient-accent1-black-left;
}
.dropdown {
background: $dark;
color: $light;
}
#content {
a {
box-shadow: inset 0 -3px 0 $accent1;
transition: box-shadow 300ms ease-in-out;
&:hover {
box-shadow: inset 0 -23px 0 $accent1;
transition: box-shadow 300ms ease-in-out;
}
}
}
table, th, td {
border: 1px solid $light;
}
th {
background: lighten($black, 15%);
}
}
/* Style *********************************************************************/
// Style //////////////////////////////////////////////////////////////////////
* {
outline: none;
}
html {
scroll-behavior: smooth;
}
body {
margin: 0;
padding: 0;
font-family: "Noto Sans Runes", "DoulosSIL", "Lato", "proxima-nova", "Helvetica Neue", Arial, sans-serif;
font-family: "Lato", "proxima-nova", Arial, sans-serif;
font-size: 1.2em;
transition: background 500ms ease-in-out, color 1s ease-in-out;
@ -346,14 +88,13 @@ header {
align-items: center;
justify-content: space-around;
min-height: 3rem;
margin-top: 1rem;
padding: 0.5rem;
top: 0;
box-shadow: rgba(2, 8, 20, 0.1) 0px 0.175em 0.5em;
transform: translateX(-40%);
transition: opacity 500ms ease-in-out, top 500ms ease-in-out;
transition: opacity 500ms ease-in-out, top 500ms ease-in-out, height 500ms ease-in-out;
}
.has-dropdown {
@ -365,11 +106,11 @@ header {
pointer-events: auto;
}
#table-of-contents {
top: $navbar-height / 1.3;
#table-of-contents, #drop-page {
top: $navbar-height;
opacity: 1;
z-index: 5;
height: 500%;
height: 80vh;
pointer-events: auto;
}
}
@ -381,11 +122,33 @@ header {
transform: translateX(-75%);
}
#drop-page {
.themeBtn {
content: ' ';
border: 2px solid white;
border-radius: 50%;
width: 1rem;
height: 1rem;
display: inline-block;
vertical-align: middle;
margin-right: 10px;
}
#drop-page, #table-of-contents {
flex-direction: column;
transform: translateX(-40%);
li {
padding: 5px;
transform: translateX(-45%);
top: -40px;
height: 0;
min-width: 300px;
overflow-y: auto;
font-size: 0.9em;
}
@media #{$switch-small-screen} {
#drop-page {
transform: translateX(-27.5%);
}
}
@ -401,7 +164,6 @@ header {
a {
width: 100%;
height: 100%;
size: 0.7rem;
padding-left: 10px;
padding-right: 10px;
font-weight: bold;
@ -409,17 +171,9 @@ header {
}
#table-of-contents {
flex-direction: column;
padding: 20px;
float: right;
overflow-y: auto;
height: 0%;
width: 75%;
min-width: 350px;
transform: translateX(-45%);
font-size: 0.9em;
top: -40px;
transition: height 500ms ease-in-out, opacity 500ms ease-in-out, top 500ms ease-in-out;
li {
@ -442,6 +196,10 @@ header {
margin: 0 auto;
text-align: justify;
@media #{$switch-small-screen} {
padding: 50px 20px;
}
a {
font-style: italic;
}
@ -481,7 +239,10 @@ header {
}
h1, h2, h3, h4, h5, h6 {
text-align: center;
font-family: serif;
font-style: italic;
margin-left: 2em;
margin-right: 60px;
}
h2 {
@ -522,7 +283,7 @@ h5 {
.tooltiptext {
visibility: hidden;
margin-left: -60px; /* Half the width */
margin-left: -20px;
bottom: 100%;
left: 10%;
padding: 5px 10px;
@ -532,7 +293,6 @@ h5 {
position: absolute;
z-index: 5;
&::after {
content: " ";
position: absolute;
@ -572,9 +332,22 @@ th, td {
padding: 10px;
}
img {
img, svg {
max-height: 600px;
max-width: 100%;
display: block;
margin: 0 auto;
}
header > img {
width: 150px;
height: 150px;
border-radius: 75px;
}
.nav-icon {
max-width: 20px;
max-height: 20px;
}
.figure {
@ -585,8 +358,72 @@ ul {
padding-inline-start: 20px;
}
pre.src {
background: $dark;
color: $light;
.org-src-container,
pre {
border-radius: .5em;
}
.org-src-container {
position: relative;
padding-left: 1em;
box-shadow: 3px 3px 10px rgba(0,0,0,.3);
min-height: 5.5em;
margin: 0 auto;
width: -moz-fit-content;
width: fit-content;
}
pre {
width: -moz-fit-content;
width: fit-content;
border: none;
box-shadow: none !important;
margin: .5em;
padding: 2.75em 1.5em !important;
overflow-y: auto;
}
pre.src::before {
top: -10px;
}
pre.src-fish::before {
content: 'fish';
}
pre.src-rust::before {
content: 'Rust';
}
.closeButton,
.minButton,
.maxButton,
.floatButton {
position: absolute;
position: absolute;
width: .8em;
height: .8em;
border-radius: .4em;
z-index: 2;
left: .3em;
}
.closeButton {
top: .3em;
}
.minButton {
top: 1.4em;
}
.maxButton {
top: 2.5em;
}
.floatButton {
bottom: .3em;
}
.twitter-tweet {
margin: 10px auto;
}

230
web/style/themes.scss Normal file
View File

@ -0,0 +1,230 @@
// Variables //////////////////////////////////////////////////////////////////
$tooltip-underline-size: 3px;
// Themes /////////////////////////////////////////////////////////////////////
$nord0: #2e3440;
$nord1: #3b4252;
$nord2: #434c5e;
$nord3: #4c566a;
$nord4: #d8dee9;
$nord5: #e5e9f0;
$nord6: #eceff4;
$nord7: #8fbcbb;
$nord8: #88c0d0;
$nord9: #81a1c1;
$nord10: #5e81ac;
$nord11: #bf616a;
$nord12: #d08770;
$nord13: #ebcb8b;
$nord14: #a3be8c;
$nord15: #b48ead;
$dark: $nord1;
$light: $nord5;
$accent1: $nord7;
$accent2: $nord9;
$accent3: $nord8;
.light {
$border-color: $accent1;
color: $dark;
background: $light;
$bg-nav: linear-gradient(to right, $nord6, $nord4);
.navbar, header, .dropdown {
background: $bg-nav;
color: $dark;
}
.navbar svg {
fill: $dark;
}
transition: background 500ms ease-in-out, color 1s ease-in-out;
pre {
border-color: $light;
}
pre.src, pre.example {
&::before {
background-color: $nord6;
color: $dark;
}
}
pre.src, pre.example, .org-src-container {
background: $light;
color: $dark;
}
.status {
background: $bg-nav;
color: $dark;
}
.tooltip {
border-bottom: $tooltip-underline-size dotted $accent3;
.tooltiptext {
background-color: $accent3;
color: $dark;
&::after {
border-color: $accent3 transparent transparent transparent;
}
}
}
#content {
a {
box-shadow: inset 0 -3px 0 $accent3;
transition: box-shadow 300ms ease-in-out;
&:hover {
box-shadow: inset 0 -23px 0 $accent3;
transition: box-shadow 300ms ease-in-out;
}
}
}
table, th, td {
border: 1px solid $dark;
}
th {
background: darken($light, 5%);
}
.gentree {
filter: invert(0%);
transition: filter 1s ease-in-out;
}
}
.black, .dark {
$border-color: $nord1;
color: $nord6;
background: $nord0;
$bg-nav: linear-gradient(to right, $nord3, $nord1);
.navbar, header, .dropdown {
background: $bg-nav;
color: $light;
}
.navbar svg {
fill: $light;
}
transition: background 500ms ease-in-out, color 1s ease-in-out;
pre {
border: none;
}
pre.src, pre.example {
&::before {
background-color: $nord0;
color: $nord6;
}
}
.status {
background: $bg-nav;
color: $nord6;
}
.tooltip {
border-bottom: $tooltip-underline-size dotted $nord7;
.tooltiptext {
background-color: $nord7;
color: $nord6;
&::after {
border-color: $nord7 transparent transparent transparent;
}
}
}
#content {
a {
box-shadow: inset 0 -3px 0 $nord7;
transition: box-shadow 300ms ease-in-out;
&:hover {
box-shadow: inset 0 -23px 0 $nord7;
transition: box-shadow 300ms ease-in-out;
}
}
}
table, th, td {
border: 1px solid $nord7;
}
th {
background: darken($nord0, 2.5%);
}
.gentree {
filter: invert(100%);
transition: filter 1s ease-in-out;
}
pre.src, pre.example, .org-src-container {
background: $nord1;
color: $nord5;
}
}
.black {
background: black;
color: $nord4;
$bg-nav: linear-gradient(to right, $nord2, $nord0);
.navbar, header, .dropdown {
background: $bg-nav;
}
pre.src, pre.example, .org-src-container {
background: $nord0;
color: $nord4;
}
}
#lightBtn {
background: $light;
}
#darkBtn {
background: $nord0;
}
#blackBtn {
background: black;
}
pre {
box-shadow: none;
}
.closeButton {
background-color: $nord11;
}
.minButton {
background-color: $nord12;
}
.maxButton {
background-color: $nord14;
}
.floatButton {
background-color: $nord15;
}