commit
9ac6e9930e
106 changed files with 17627 additions and 0 deletions
@ -0,0 +1,8 @@ |
||||
.idea |
||||
__pycache__/ |
||||
venv |
||||
db/labertasche.db-shm |
||||
db/labertasche.db-wal |
||||
output |
||||
/output/ |
||||
*.sql |
@ -0,0 +1,22 @@ |
||||
MIT License |
||||
|
||||
Copyright (c) 2020 Domeniko Gentner <code@tuxstash.de> |
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy |
||||
of this software and associated documentation files (the "Software"), to deal |
||||
in the Software without restriction, including without limitation the rights |
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell |
||||
copies of the Software, and to permit persons to whom the Software is |
||||
furnished to do so, subject to the following conditions: |
||||
|
||||
The above copyright notice and this permission notice shall be included in all |
||||
copies or substantial portions of the Software. |
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE |
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER |
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, |
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE |
||||
SOFTWARE. |
||||
|
@ -0,0 +1,20 @@ |
||||
[[source]] |
||||
name = "pypi" |
||||
url = "https://pypi.org/simple" |
||||
verify_ssl = true |
||||
|
||||
[dev-packages] |
||||
|
||||
[packages] |
||||
flask = "*" |
||||
pyyaml = "*" |
||||
flask-sqlalchemy = "*" |
||||
flask-cors = "*" |
||||
antispam = "*" |
||||
flask-login = "*" |
||||
sqlalchemy = "*" |
||||
requests = "*" |
||||
py3-validate-email = "*" |
||||
|
||||
[requires] |
||||
python_version = "3.8" |
@ -0,0 +1,251 @@ |
||||
{ |
||||
"_meta": { |
||||
"hash": { |
||||
"sha256": "57134ef6f8a30aa46c1ab6263e62e14edbb27d6df2911fc6b2140dde8c49d27c" |
||||
}, |
||||
"pipfile-spec": 6, |
||||
"requires": { |
||||
"python_version": "3.8" |
||||
}, |
||||
"sources": [ |
||||
{ |
||||
"name": "pypi", |
||||
"url": "https://pypi.org/simple", |
||||
"verify_ssl": true |
||||
} |
||||
] |
||||
}, |
||||
"default": { |
||||
"antispam": { |
||||
"hashes": [ |
||||
"sha256:e188b424ea9b76c408a592a5ff60eb1280f45f26b404db4d5e96123f485de39b" |
||||
], |
||||
"index": "pypi", |
||||
"version": "==0.0.10" |
||||
}, |
||||
"certifi": { |
||||
"hashes": [ |
||||
"sha256:1f422849db327d534e3d0c5f02a263458c3955ec0aae4ff09b95f195c59f4edd", |
||||
"sha256:f05def092c44fbf25834a51509ef6e631dc19765ab8a57b4e7ab85531f0a9cf4" |
||||
], |
||||
"version": "==2020.11.8" |
||||
}, |
||||
"chardet": { |
||||
"hashes": [ |
||||
"sha256:84ab92ed1c4d4f16916e05906b6b75a6c0fb5db821cc65e70cbd64a3e2a5eaae", |
||||
"sha256:fc323ffcaeaed0e0a02bf4d117757b98aed530d9ed4531e3e15460124c106691" |
||||
], |
||||
"version": "==3.0.4" |
||||
}, |
||||
"click": { |
||||
"hashes": [ |
||||
"sha256:d2b5255c7c6349bc1bd1e59e08cd12acbbd63ce649f2588755783aa94dfb6b1a", |
||||
"sha256:dacca89f4bfadd5de3d7489b7c8a566eee0d3676333fbb50030263894c38c0dc" |
||||
], |
||||
"version": "==7.1.2" |
||||
}, |
||||
"dnspython": { |
||||
"hashes": [ |
||||
"sha256:044af09374469c3a39eeea1a146e8cac27daec951f1f1f157b1962fc7cb9d1b7", |
||||
"sha256:40bb3c24b9d4ec12500f0124288a65df232a3aa749bb0c39734b782873a2544d" |
||||
], |
||||
"version": "==2.0.0" |
||||
}, |
||||
"filelock": { |
||||
"hashes": [ |
||||
"sha256:18d82244ee114f543149c66a6e0c14e9c4f8a1044b5cdaadd0f82159d6a6ff59", |
||||
"sha256:929b7d63ec5b7d6b71b0fa5ac14e030b3f70b75747cef1b10da9b879fef15836" |
||||
], |
||||
"version": "==3.0.12" |
||||
}, |
||||
"flask": { |
||||
"hashes": [ |
||||
"sha256:4efa1ae2d7c9865af48986de8aeb8504bf32c7f3d6fdc9353d34b21f4b127060", |
||||
"sha256:8a4fdd8936eba2512e9c85df320a37e694c93945b33ef33c89946a340a238557" |
||||
], |
||||
"index": "pypi", |
||||
"version": "==1.1.2" |
||||
}, |
||||
"flask-cors": { |
||||
"hashes": [ |
||||
"sha256:6bcfc100288c5d1bcb1dbb854babd59beee622ffd321e444b05f24d6d58466b8", |
||||
"sha256:cee4480aaee421ed029eaa788f4049e3e26d15b5affb6a880dade6bafad38324" |
||||
], |
||||
"index": "pypi", |
||||
"version": "==3.0.9" |
||||
}, |
||||
"flask-login": { |
||||
"hashes": [ |
||||
"sha256:6d33aef15b5bcead780acc339464aae8a6e28f13c90d8b1cf9de8b549d1c0b4b", |
||||
"sha256:7451b5001e17837ba58945aead261ba425fdf7b4f0448777e597ddab39f4fba0" |
||||
], |
||||
"index": "pypi", |
||||
"version": "==0.5.0" |
||||
}, |
||||
"flask-sqlalchemy": { |
||||
"hashes": [ |
||||
"sha256:05b31d2034dd3f2a685cbbae4cfc4ed906b2a733cff7964ada450fd5e462b84e", |
||||
"sha256:bfc7150eaf809b1c283879302f04c42791136060c6eeb12c0c6674fb1291fae5" |
||||
], |
||||
"index": "pypi", |
||||
"version": "==2.4.4" |
||||
}, |
||||
"idna": { |
||||
"hashes": [ |
||||
"sha256:b307872f855b18632ce0c21c5e45be78c0ea7ae4c15c828c20788b26921eb3f6", |
||||
"sha256:b97d804b1e9b523befed77c48dacec60e6dcb0b5391d57af6a65a312a90648c0" |
||||
], |
||||
"version": "==2.10" |
||||
}, |
||||
"itsdangerous": { |
||||
"hashes": [ |
||||
"sha256:321b033d07f2a4136d3ec762eac9f16a10ccd60f53c0c91af90217ace7ba1f19", |
||||
"sha256:b12271b2047cb23eeb98c8b5622e2e5c5e9abd9784a153e9d8ef9cb4dd09d749" |
||||
], |
||||
"version": "==1.1.0" |
||||
}, |
||||
"jinja2": { |
||||
"hashes": [ |
||||
"sha256:89aab215427ef59c34ad58735269eb58b1a5808103067f7bb9d5836c651b3bb0", |
||||
"sha256:f0a4641d3cf955324a89c04f3d94663aa4d638abe8f733ecd3582848e1c37035" |
||||
], |
||||
"version": "==2.11.2" |
||||
}, |
||||
"markupsafe": { |
||||
"hashes": [ |
||||
"sha256:00bc623926325b26bb9605ae9eae8a215691f33cae5df11ca5424f06f2d1f473", |
||||
"sha256:09027a7803a62ca78792ad89403b1b7a73a01c8cb65909cd876f7fcebd79b161", |
||||
"sha256:09c4b7f37d6c648cb13f9230d847adf22f8171b1ccc4d5682398e77f40309235", |
||||
"sha256:1027c282dad077d0bae18be6794e6b6b8c91d58ed8a8d89a89d59693b9131db5", |
||||
"sha256:13d3144e1e340870b25e7b10b98d779608c02016d5184cfb9927a9f10c689f42", |
||||
"sha256:24982cc2533820871eba85ba648cd53d8623687ff11cbb805be4ff7b4c971aff", |
||||
"sha256:29872e92839765e546828bb7754a68c418d927cd064fd4708fab9fe9c8bb116b", |
||||
"sha256:43a55c2930bbc139570ac2452adf3d70cdbb3cfe5912c71cdce1c2c6bbd9c5d1", |
||||
"sha256:46c99d2de99945ec5cb54f23c8cd5689f6d7177305ebff350a58ce5f8de1669e", |
||||
"sha256:500d4957e52ddc3351cabf489e79c91c17f6e0899158447047588650b5e69183", |
||||
"sha256:535f6fc4d397c1563d08b88e485c3496cf5784e927af890fb3c3aac7f933ec66", |
||||
"sha256:596510de112c685489095da617b5bcbbac7dd6384aeebeda4df6025d0256a81b", |
||||
"sha256:62fe6c95e3ec8a7fad637b7f3d372c15ec1caa01ab47926cfdf7a75b40e0eac1", |
||||
"sha256:6788b695d50a51edb699cb55e35487e430fa21f1ed838122d722e0ff0ac5ba15", |
||||
"sha256:6dd73240d2af64df90aa7c4e7481e23825ea70af4b4922f8ede5b9e35f78a3b1", |
||||
"sha256:717ba8fe3ae9cc0006d7c451f0bb265ee07739daf76355d06366154ee68d221e", |
||||
"sha256:79855e1c5b8da654cf486b830bd42c06e8780cea587384cf6545b7d9ac013a0b", |
||||
"sha256:7c1699dfe0cf8ff607dbdcc1e9b9af1755371f92a68f706051cc8c37d447c905", |
||||
"sha256:88e5fcfb52ee7b911e8bb6d6aa2fd21fbecc674eadd44118a9cc3863f938e735", |
||||
"sha256:8defac2f2ccd6805ebf65f5eeb132adcf2ab57aa11fdf4c0dd5169a004710e7d", |
||||
"sha256:98c7086708b163d425c67c7a91bad6e466bb99d797aa64f965e9d25c12111a5e", |
||||
"sha256:9add70b36c5666a2ed02b43b335fe19002ee5235efd4b8a89bfcf9005bebac0d", |
||||
"sha256:9bf40443012702a1d2070043cb6291650a0841ece432556f784f004937f0f32c", |
||||
"sha256:ade5e387d2ad0d7ebf59146cc00c8044acbd863725f887353a10df825fc8ae21", |
||||
"sha256:b00c1de48212e4cc9603895652c5c410df699856a2853135b3967591e4beebc2", |
||||
"sha256:b1282f8c00509d99fef04d8ba936b156d419be841854fe901d8ae224c59f0be5", |
||||
"sha256:b2051432115498d3562c084a49bba65d97cf251f5a331c64a12ee7e04dacc51b", |
||||
"sha256:ba59edeaa2fc6114428f1637ffff42da1e311e29382d81b339c1817d37ec93c6", |
||||
"sha256:c8716a48d94b06bb3b2524c2b77e055fb313aeb4ea620c8dd03a105574ba704f", |
||||
"sha256:cd5df75523866410809ca100dc9681e301e3c27567cf498077e8551b6d20e42f", |
||||
"sha256:cdb132fc825c38e1aeec2c8aa9338310d29d337bebbd7baa06889d09a60a1fa2", |
||||
"sha256:e249096428b3ae81b08327a63a485ad0878de3fb939049038579ac0ef61e17e7", |
||||
"sha256:e8313f01ba26fbbe36c7be1966a7b7424942f670f38e666995b88d012765b9be" |
||||
], |
||||
"version": "==1.1.1" |
||||
}, |
||||
"py3-validate-email": { |
||||
"hashes": [ |
||||
"sha256:3bbb264b49c0ae09afdb2738956f00b0e8dd7e079e2d079b2e9b6688de474d28" |
||||
], |
||||
"index": "pypi", |
||||
"version": "==0.2.10" |
||||
}, |
||||
"pyyaml": { |
||||
"hashes": [ |
||||
"sha256:06a0d7ba600ce0b2d2fe2e78453a470b5a6e000a985dd4a4e54e436cc36b0e97", |
||||
"sha256:240097ff019d7c70a4922b6869d8a86407758333f02203e0fc6ff79c5dcede76", |
||||
"sha256:4f4b913ca1a7319b33cfb1369e91e50354d6f07a135f3b901aca02aa95940bd2", |
||||
"sha256:69f00dca373f240f842b2931fb2c7e14ddbacd1397d57157a9b005a6a9942648", |
||||
"sha256:73f099454b799e05e5ab51423c7bcf361c58d3206fa7b0d555426b1f4d9a3eaf", |
||||
"sha256:74809a57b329d6cc0fdccee6318f44b9b8649961fa73144a98735b0aaf029f1f", |
||||
"sha256:7739fc0fa8205b3ee8808aea45e968bc90082c10aef6ea95e855e10abf4a37b2", |
||||
"sha256:95f71d2af0ff4227885f7a6605c37fd53d3a106fcab511b8860ecca9fcf400ee", |
||||
"sha256:b8eac752c5e14d3eca0e6dd9199cd627518cb5ec06add0de9d32baeee6fe645d", |
||||
"sha256:cc8955cfbfc7a115fa81d85284ee61147059a753344bc51098f3ccd69b0d7e0c", |
||||
"sha256:d13155f591e6fcc1ec3b30685d50bf0711574e2c0dfffd7644babf8b5102ca1a" |
||||
], |
||||
"index": "pypi", |
||||
"version": "==5.3.1" |
||||
}, |
||||
"requests": { |
||||
"hashes": [ |
||||
"sha256:7f1a0b932f4a60a1a65caa4263921bb7d9ee911957e0ae4a23a6dd08185ad5f8", |
||||
"sha256:e786fa28d8c9154e6a4de5d46a1d921b8749f8b74e28bde23768e5e16eece998" |
||||
], |
||||
"index": "pypi", |
||||
"version": "==2.25.0" |
||||
}, |
||||
"six": { |
||||
"hashes": [ |
||||
"sha256:30639c035cdb23534cd4aa2dd52c3bf48f06e5f4a941509c8bafd8ce11080259", |
||||
"sha256:8b74bedcbbbaca38ff6d7491d76f2b06b3592611af620f8426e82dddb04a5ced" |
||||
], |
||||
"version": "==1.15.0" |
||||
}, |
||||
"sqlalchemy": { |
||||
"hashes": [ |
||||
"sha256:009e8388d4d551a2107632921320886650b46332f61dc935e70c8bcf37d8e0d6", |
||||
"sha256:0157c269701d88f5faf1fa0e4560e4d814f210c01a5b55df3cab95e9346a8bcc", |
||||
"sha256:0a92745bb1ebbcb3985ed7bda379b94627f0edbc6c82e9e4bac4fb5647ae609a", |
||||
"sha256:0cca1844ba870e81c03633a99aa3dc62256fb96323431a5dec7d4e503c26372d", |
||||
"sha256:166917a729b9226decff29416f212c516227c2eb8a9c9f920d69ced24e30109f", |
||||
"sha256:1f5f369202912be72fdf9a8f25067a5ece31a2b38507bb869306f173336348da", |
||||
"sha256:2909dffe5c9a615b7e6c92d1ac2d31e3026dc436440a4f750f4749d114d88ceb", |
||||
"sha256:2b5dafed97f778e9901b79cc01b88d39c605e0545b4541f2551a2fd785adc15b", |
||||
"sha256:2e9bd5b23bba8ae8ce4219c9333974ff5e103c857d9ff0e4b73dc4cb244c7d86", |
||||
"sha256:3aa6d45e149a16aa1f0c46816397e12313d5e37f22205c26e06975e150ffcf2a", |
||||
"sha256:4bdbdb8ca577c6c366d15791747c1de6ab14529115a2eb52774240c412a7b403", |
||||
"sha256:53fd857c6c8ffc0aa6a5a3a2619f6a74247e42ec9e46b836a8ffa4abe7aab327", |
||||
"sha256:5cdfe54c1e37279dc70d92815464b77cd8ee30725adc9350f06074f91dbfeed2", |
||||
"sha256:5d92c18458a4aa27497a986038d5d797b5279268a2de303cd00910658e8d149c", |
||||
"sha256:632b32183c0cb0053194a4085c304bc2320e5299f77e3024556fa2aa395c2a8b", |
||||
"sha256:7c735c7a6db8ee9554a3935e741cf288f7dcbe8706320251eb38c412e6a4281d", |
||||
"sha256:7cd40cb4bc50d9e87b3540b23df6e6b24821ba7e1f305c1492b0806c33dbdbec", |
||||
"sha256:84f0ac4a09971536b38cc5d515d6add7926a7e13baa25135a1dbb6afa351a376", |
||||
"sha256:8dcbf377529a9af167cbfc5b8acec0fadd7c2357fc282a1494c222d3abfc9629", |
||||
"sha256:950f0e17ffba7a7ceb0dd056567bc5ade22a11a75920b0e8298865dc28c0eff6", |
||||
"sha256:9e379674728f43a0cd95c423ac0e95262500f9bfd81d33b999daa8ea1756d162", |
||||
"sha256:b15002b9788ffe84e42baffc334739d3b68008a973d65fad0a410ca5d0531980", |
||||
"sha256:b6f036ecc017ec2e2cc2a40615b41850dc7aaaea6a932628c0afc73ab98ba3fb", |
||||
"sha256:bad73f9888d30f9e1d57ac8829f8a12091bdee4949b91db279569774a866a18e", |
||||
"sha256:bbc58fca72ce45a64bb02b87f73df58e29848b693869e58bd890b2ddbb42d83b", |
||||
"sha256:bca4d367a725694dae3dfdc86cf1d1622b9f414e70bd19651f5ac4fb3aa96d61", |
||||
"sha256:be41d5de7a8e241864189b7530ca4aaf56a5204332caa70555c2d96379e18079", |
||||
"sha256:bf53d8dddfc3e53a5bda65f7f4aa40fae306843641e3e8e701c18a5609471edf", |
||||
"sha256:c092fe282de83d48e64d306b4bce03114859cdbfe19bf8a978a78a0d44ddadb1", |
||||
"sha256:c3ab23ee9674336654bf9cac30eb75ac6acb9150dc4b1391bec533a7a4126471", |
||||
"sha256:ce64a44c867d128ab8e675f587aae7f61bd2db836a3c4ba522d884cd7c298a77", |
||||
"sha256:d05cef4a164b44ffda58200efcb22355350979e000828479971ebca49b82ddb1", |
||||
"sha256:d2f25c7f410338d31666d7ddedfa67570900e248b940d186b48461bd4e5569a1", |
||||
"sha256:d3b709d64b5cf064972b3763b47139e4a0dc4ae28a36437757f7663f67b99710", |
||||
"sha256:e32e3455db14602b6117f0f422f46bc297a3853ae2c322ecd1e2c4c04daf6ed5", |
||||
"sha256:ed53209b5f0f383acb49a927179fa51a6e2259878e164273ebc6815f3a752465", |
||||
"sha256:f605f348f4e6a2ba00acb3399c71d213b92f27f2383fc4abebf7a37368c12142", |
||||
"sha256:fcdb3755a7c355bc29df1b5e6fb8226d5c8b90551d202d69d0076a8a5649d68b" |
||||
], |
||||
"index": "pypi", |
||||
"version": "==1.3.20" |
||||
}, |
||||
"urllib3": { |
||||
"hashes": [ |
||||
"sha256:19188f96923873c92ccb987120ec4acaa12f0461fa9ce5d3d0772bc965a39e08", |
||||
"sha256:d8ff90d979214d7b4f8ce956e80f4028fc6860e4431f731ea4a8c08f23f99473" |
||||
], |
||||
"version": "==1.26.2" |
||||
}, |
||||
"werkzeug": { |
||||
"hashes": [ |
||||
"sha256:2de2a5db0baeae7b2d2664949077c2ac63fbd16d98da0ff71837f7d1dea3fd43", |
||||
"sha256:6c80b1e5ad3665290ea39320b91e1be1e0d5f60652b964a3070216de83d2e47c" |
||||
], |
||||
"version": "==1.0.1" |
||||
} |
||||
}, |
||||
"develop": {} |
||||
} |
@ -0,0 +1,130 @@ |
||||
# Labertasche |
||||
|
||||
A comment system for Hugo, written in Python (and Javascript). |
||||
|
||||
## Feature Set |
||||
|
||||
* Written in Python, utilizing Flask |
||||
* Robust Database handling by utilizing SQLAlchemy, which supports all big database engines |
||||
* flask-cors for robust security |
||||
* Uses Javascript to send comment via POST to the comment server |
||||
* Has callbacks for implementing your own notifications during the posting process. |
||||
* No IP being logged |
||||
* Email confirmation |
||||
* EMail Blocklist |
||||
* Only outputs JSON, so templates can be done independently, enhancing customization. Using the comments via a partial |
||||
template in Hugo is the recommended way. See below for integration code. |
||||
|
||||
|
||||
## Requirements |
||||
|
||||
* A public webserver capable of running Apache/NGINX and/or gunicorn. This server does not need to be the same as the |
||||
server running the site, but it must have access to your CI/CD chain. Same server is of course easier to implement. |
||||
|
||||
|
||||
## How does it work? |
||||
|
||||
A picture often says more than a thousand words: |
||||
|
||||
![flow chart image](/docs/flow-chart.png "Flow Chart") |
||||
|
||||
In some words, the user sends the comment from your site to the comment system, the comment system does the validation |
||||
and confirmation. Then, a json is put into the data directory from where you can load it via Hugo and generate your |
||||
template. |
||||
|
||||
## Setup |
||||
|
||||
Run `ssh://git@git.tuxstash.de:1235/gothseidank/labertasche.git` in the directory where you wish to host the comment |
||||
system. For example, `/var/www/html`, I also recommend making use of `/srv/` or `/opt/`. It depends on you. |
||||
|
||||
When everything is downloaded, create the directory `/etc/labertasche`. In this directory, we need 2 files: |
||||
|
||||
* labertasche.yaml - you can find an example in the root directory. |
||||
* mail_credentials.json - you can find an example in the root directory. |
||||
|
||||
Copy these files from the root directory of this app to the folder `/etc/labertasche`. Make sure to set ownership for |
||||
your user that runs your server later. I always do `chmown user:www-data`, so Apache has only group rights and enable read-only |
||||
for the Apache user. |
||||
|
||||
Make sure to read the config and replace the values as needed. The mail configuration should need no explanation, |
||||
`labertasche.yaml` has comments. Feel free to bug about more documentation regarding this. Pay special attention to |
||||
secrets and passwords. |
||||
|
||||
Now, for the server there are several options. I personally always host flask apps with Apache and mod_wsgi. |
||||
The config looks like this: |
||||
|
||||
* [Apache](docs/apache-config.md) |
||||
|
||||
Other options: |
||||
|
||||
* [gunicorn](https://gunicorn.org/https://gunicorn.org/) + Apache/Nginx with Proxy Pass |
||||
|
||||
Once you can see the administrative page, you can start integrating it into Hugo. |
||||
|
||||
## Integrating it into Hugo |
||||
|
||||
### Javascript |
||||
|
||||
In the project folder is a small javascript file. You will need to load this into Hugo, I suggest using Hugo's asset |
||||
pipeline to integrate it into your site. One thing is important to know: this script only does the bare bones post request |
||||
to your comment backend. Anything else must be done by yourself, but don't worry: The function is making use of a callback, |
||||
so you can control what happens during the various stages. |
||||
|
||||
TODO: Example using the javascript properly |
||||
|
||||
### Hugo templates |
||||
|
||||
Remember the `labertasche.yaml` file? It asked you where the data folder of Hugo is. What this program does, is to place |
||||
various json files into that folder, in folders that describe your sections. So, for each category/section of your blog |
||||
where comments can be placed, one folder will be made. And for each page within that section it generates a json file. |
||||
|
||||
Now create a new partial called "comments.html" (or something else). Within that template the following structure is needed: |
||||
|
||||
``` |
||||
{{ $location := .Scratch.Get "location" }} |
||||
{{ if (fileExists $location ) }} |
||||
{{ $dataJ := getJSON $location }} |
||||
{{ range $dataJ.comments }} |
||||
|
||||
{{ end }} |
||||
{{ end }} |
||||
``` |
||||
|
||||
This loads the json depending on the rel url and walks the list of comments. You can then use the following variables to |
||||
access the per-comment data: |
||||
|
||||
* .content => The body of the message the user has sent |
||||
* .email => The mail the person used to send the mail |
||||
* .created_on => The date and time the comment was posted |
||||
* .comment_id => The comment id, great for making anchors |
||||
* .gravatar => The md5 hash of the mail for gravatar, if caching is on, prepend e.g. `/images`, otherwise use the gravatar url to integrate it. |
||||
|
||||
You can style around them as needed. You have free reign. |
||||
|
||||
Of course you will also need a few inputs and a button that submits the data. |
||||
Here is a base skeleton to start out: |
||||
|
||||
``` |
||||
<div> |
||||
<input type="text" maxlength=100 placeholder="Enter Email" id="labertasche-mail"> |
||||
<textarea cols="10" rows="10" id="labertasche-text"></textarea> |
||||
<input type="button" onclick="labertasche_post_comment(this, labertasche_callback);"> |
||||
</div> |
||||
``` |
||||
|
||||
Please take note of the `id` on each element, these are mandatory, as well as the function call for the `onclick` event. |
||||
Again, style as needed and add more Javascript to your gusto. |
||||
|
||||
|
||||
Inside your template `single.html`, or wherever you want to place comments, you qwill also need this: |
||||
|
||||
``` |
||||
{{ $file := replaceRE "^(.*)[\\/]$" "data$1.json" .Page.RelPermalink }} |
||||
{{ .Scratch.Set "location" $file }} |
||||
{{ partial "partials/comments" . }} |
||||
``` |
||||
|
||||
After that and configuring labertasche correctly, the json files should be placed in your data folder and all you got |
||||
to do after that, is to rebuild Hugo and the new comment should appear. |
||||
|
||||
|
@ -0,0 +1,35 @@ |
||||
This is an example server config for Apache Webserver with mod_wsgi. |
||||
If you wish to use pipenv, then please take also a look at the |
||||
[WSGIPythonHome](https://modwsgi.readthedocs.io/en/develop/configuration-directives/WSGIPythonHome.html) |
||||
directive. |
||||
|
||||
``` |
||||
<VirtualHost *:80> |
||||
ServerAdmin server@example.com |
||||
ServerName comments.example.com |
||||
Redirect permanent / https://comments.example.com |
||||
</VirtualHost> |
||||
|
||||
|
||||
<VirtualHost *:443> |
||||
ServerAdmin server@example.com |
||||
ServerName comments.example.com |
||||
|
||||
WSGIDaemonProcess laberflask user=user group=group threads=2 |
||||
WSGIScriptAlias / /var/www/html/labertasche/server.wsgi |
||||
|
||||
SSLCertificateFile /etc/letsencrypt/live/comments.example.com/fullchain.pem |
||||
SSLCertificateKeyFile /etc/letsencrypt/live/comments.example.com/privkey.pem |
||||
Include /etc/letsencrypt/options-ssl-apache.conf |
||||
|
||||
<Directory "/var/www/html/labertasche"> |
||||
WSGIProcessGroup laberflask |
||||
WSGIApplicationGroup %{GLOBAL} |
||||
Options -Indexes |
||||
AllowOverride None |
||||
Require all granted |
||||
</Directory> |
||||
ErrorLog ${APACHE_LOG_DIR}/laberflask.error.log |
||||
CustomLog /dev/null common |
||||
</VirtualHost> |
||||
``` |
@ -0,0 +1 @@ |
||||
<mxfile host="app.diagrams.net" modified="2020-11-15T15:20:17.771Z" agent="5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/86.0.4240.193 Safari/537.36" etag="1QcwDtM_10p-pEkd1fcO" version="13.9.8" type="device"><diagram id="C5RBs43oDa-KdzZeNtuy" name="Page-1">7Vttc9o4EP41fMyNLdnGfIVAm14uyZX00ny6kW2BXYTFWSJAf/1JtmxeRBsnBQSezjCMtJZsa3ef1b7ILdibLj9kaBb/RSNMWsCKli143QLAdgBoyZ8VrQpK21OEcZZEatCaMEy+Y0W0FHWeRJhtDeSUEp7MtokhTVMc8i0ayjK62B42omT7qTM0xhphGCKiU5+SiMcF1QftNf0jTsZx+WTb6xRXpqgcrFbCYhTRxQYJ9luwl1HKi9Z02cNEMq/ky9PN6oncTrwPn/5m/6Ev3T8f7/65Km42eMuUagkZTvm7b02GV3Nr6PUG7FO3C1CPsvbkylFL46uSXzgS7FNdmvGYjmmKSH9N7WZ0nkZY3tUSvfWYW0pngmgL4jfM+UrpAppzKkgxnxJ1FS8T/lVNl+1n2f7DVb3r5cal61XZSXm2+lreQHY2ZsnuelreK+cV65OL2lGHV3ipxjE6z0L8EwYq3eAoG2P+k3GgUhiBNEynWLykmJdhgnjysv1ySKn8uBq3FqtoKMm+QcqeUSlXkn3euPKKlDcE+7wl1/OWsmtSyuolXxCZqycNCGITQRri7AVnmhKsRSylsogTjoczlDNiITaDbXGOEkJ6lNAsnwuxHbm4LeiMZ3SCN650vDZEXiUU8WCOl+8Qi85GdRfgq5WqHclR3cXavNueosUbpr2cdnDGA43xn7FYNsOC+JDR5aopnHfPjPG2rXH2t2F7k2Fzaxo229qvGKexbK4GMHGbkE6ngglMNFO8aAGPiAV0A2HmvLFsPdwPHw8KvAhhfxTuA54X+jgYndDkVTAzBz2jnqPdeofnaLXOynO06zoVNjSJPdu9lBDhfARm1Nm3dT8wYXJ9MzRtwYFuEmM6DebszeZwNBqBcK85jLzAc4/ph0CwbQ4rM7dhDv1TWkMImu6I5L0HnCWCYTg7FeJATcQBzyji9ADgjh4MZ8Lb9yNnH858EEDvmDizzw5nF+PwHx0asO5m5BuFBtSg8YzZ4fYgP8T796DAdx3XOiI23HPDRpnRPn9sXNYe5F3GHuRpQGPidcUs9m/u+cmyi/zLxGUtVk5SJgDSkkWPJH9Umg8OCA0nJGFcB+yW3lwMaHcdR88yHUcDoztaI+Jovy5AjeawgNkQ4T2CPrNSW21B2x2jltjXLHEefyepfBohdCEECqzcrDYjHN91hZw9hYHTukKw6a7Q0bHWqYs1s15PR8NaMyJv6J0bpMymgRsAKVA3fQwco36Knj5uSMTu7Z4bMI4pv+mYMhKxA6cu0IzuXeVrbgAt9w51qP1CrG1wC/Occ4u14eWcgxOdk8OmbqILGj3l5hgV4psSJnuEeK5JlNrCd8w6J3qW8+Zu2P/8KGg3d4/3embzuqupC4vRTDbDFUmEVmTwdasaFPpzG1QEFE7GuVbdz7m4DVZ0ViiQ7eqm2OC5Pbs8xFGd4ddNcWePJa7qf4dH8e8I/RcRC+uGE9BoARDq4UR/OaN5sSGvMXycj2nxYGB9Y1QmyZKR+EupHFLUMFB1fuWgvpG583zuTnjf3lM8PLFvpJ+xHOA8USlNHU6jprB+JwjsGD9J6ZgtDDTBFNYtDDg/UI0TmUK9MKAMHqEoKrC2KvjCE0SaArhdW9cBxhFna4LozhMiBfCEAyb4KpUpFvwex6LVEwMGvZuDysPkRxw78rAt499xOEaTy00ogjt1c17QaBHc0XNeX5j8Mk1+/jtKsinLm/mHHaU5LI6kHBB7oxH2flApbXcC66jnT+zT+X2iu/42Ob+28YU37P8P</diagram></mxfile> |
After Width: | Height: | Size: 53 KiB |
@ -0,0 +1,84 @@ |
||||
//**********************************************************************************
|
||||
// * _author : Domeniko Gentner
|
||||
// * _mail : code@tuxstash.de
|
||||
// * _repo : https://git.tuxstash.de/gothseidank/labertasche
|
||||
// * _license : This project is under MIT License
|
||||
// *********************************************************************************/
|
||||
|
||||
/* |
||||
Callback example. |
||||
Possible messages: |
||||
|
||||
post-min-length |
||||
post-max-length |
||||
post-invalid-json |
||||
post-duplicate |
||||
post-internal-server-error |
||||
post-success |
||||
post-before-fetch |
||||
|
||||
function labertasche_callback(state) |
||||
{ |
||||
if (state === "post-before-fetch"){ |
||||
|
||||
} |
||||
if (state === "post-min-length"){ |
||||
|
||||
} |
||||
if (state === "post-success"){ |
||||
|
||||
} |
||||
if (state === "post-fetch-exception" || state === "post-internal-server-error"){ |
||||
|
||||
} |
||||
if (state === "post-invalid-email"){ |
||||
|
||||
} |
||||
} |
||||
*/ |
||||
|
||||
function labertasche_post_comment(btn, callback) |
||||
{ |
||||
let remote = document.getElementById('labertasche-comment-section').dataset.remote; |
||||
let comment = document.getElementById('labertasche-text').value; |
||||
let mail = document.getElementById('labertasche-mail').value; |
||||
|
||||
if (mail.length <= 0 || comment.length < 40){ |
||||
callback('post-min-length'); |
||||
if(btn) { |
||||
btn.preventDefault(); |
||||
} |
||||
return |
||||
} |
||||
|
||||
callback('post-before-fetch'); |
||||
fetch(remote, |
||||
{ |
||||
mode:"cors", |
||||
headers: { |
||||
'Access-Control-Allow-Origin':'*', |
||||
'Accept': 'application/json', |
||||
'Content-Type': 'application/json' |
||||
}, |
||||
method: "POST", |
||||
// use real location
|
||||
body: JSON.stringify({ "email": mail, |
||||
"content": comment, |
||||
"location": window.location.pathname, |
||||
"replied_to": null // TODO: future feature: replies?
|
||||
}) |
||||
}) |
||||
.then(async function(response){ |
||||
let result = await response.json(); |
||||
callback(result['status']); |
||||
}) |
||||
.catch(function(exc){ |
||||
console.log(exc); |
||||
callback('post-fetch-exception'); |
||||
}) |
||||
|
||||
// Don't reload the page
|
||||
if (btn) { |
||||
btn.preventDefault(); |
||||
} |
||||
} |
@ -0,0 +1,44 @@ |
||||
# /********************************************************************************** |
||||
# * _author : Domeniko Gentner |
||||
# * _mail : code@tuxstash.de |
||||
# * _repo : https://git.tuxstash.de/gothseidank/labertasche |
||||
# * _license : This project is under MIT License |
||||
# *********************************************************************************/ |
||||
|
||||
system: |
||||
web_url: "http://dev.localhost:1314/" # Url where the comment system is served |
||||
blog_url: "http://dev.localhost:1313/" # Url of your website |
||||
cookie-domain: "dev.localhost" # Url where the comment system is served |
||||
database_uri: "sqlite:///db/labertasche.db" # Database URI. See documentation. Default is sqlite. |
||||
secret: "6Gxvb52bIJCm2vfDsmWKzShKp1omrzVG" # CHANGE ME! THIS IS IMPORTANT! |
||||
output: "../../web/tuxstash.de/data/" # Base path for the output json |
||||
debug: false # Leave this as is, this is for development. |
||||
send_otp_to_publish: true # Disables confirmation w/ OTP via mail |
||||
|
||||
gravatar: |
||||
cache: true # Enable caching of gravatar images |
||||
static_dir: "../../web/tuxstash.de/static/images/gravatar/" # Where to store cached images |
||||
size: 256 # only applies if images are cached, |
||||
# otherwise use ?s=size at the end of the gravatar url |
||||
|
||||
dashboard: |
||||
username: "admin" # CHANGE ME! |
||||
password: "admin" # CHANGE ME! |
||||
|
||||
addons: |
||||
smileys: true # Enable smiley replacements, set to false if unwanted |
||||
|
||||
# https://www.w3schools.com/charsets/ref_emoji_smileys.asp |
||||
smileys: |
||||
":)": "😀" |
||||
":d": "😁" |
||||
":D": "😁" |
||||
";)": "😉" |
||||
":p": "😋" |
||||
":P": "😋" |
||||
":8": "😎" |
||||
"(:": "🙃" |
||||
"$)": "🤑" |
||||
":o": "😲" |
||||
":O": "😲" |
||||
|
@ -0,0 +1,26 @@ |
||||
#!/usr/bin/env python3 |
||||
# -*- coding: utf-8 -*- |
||||
# /********************************************************************************** |
||||
# * _author : Domeniko Gentner |
||||
# * _mail : code@tuxstash.de |
||||
# * _repo : https://git.tuxstash.de/gothseidank/labertasche |
||||
# * _license : This project is under MIT License |
||||
# *********************************************************************************/ |
||||
|
||||
from labertasche import ( |
||||
models, |
||||
database, |
||||
blueprints, |
||||
helper, |
||||
mail, |
||||
settings |
||||
) |
||||
|
||||
_all_ = [ |
||||
models, |
||||
database, |
||||
blueprints, |
||||
helper, |
||||
mail, |
||||
settings |
||||
] |
@ -0,0 +1,12 @@ |
||||
#!/usr/bin/env python3 |
||||
# -*- coding: utf-8 -*- |
||||
# /********************************************************************************** |
||||
# * _author : Domeniko Gentner |
||||
# * _mail : code@tuxstash.de |
||||
# * _repo : https://git.tuxstash.de/gothseidank/labertasche |
||||
# * _license : This project is under MIT License |
||||
# *********************************************************************************/ |
||||
from .bp_comments import bp_comments |
||||
from .bp_login import bp_login |
||||
from .bp_dashboard import bp_dashboard |
||||
|
@ -0,0 +1,189 @@ |
||||
#!/usr/bin/env python3 |
||||
# -*- coding: utf-8 -*- |
||||
# /********************************************************************************** |
||||
# * _author : Domeniko Gentner |
||||
# * _mail : code@tuxstash.de |
||||
# * _repo : https://git.tuxstash.de/gothseidank/labertasche |
||||
# * _license : This project is under MIT License |
||||
# *********************************************************************************/ |
||||
import re |
||||
from sys import stderr |
||||
from antispam import is_spam as spam, score |
||||
from flask import Blueprint, jsonify, request, make_response, redirect |
||||
from flask_cors import cross_origin |
||||
from sqlalchemy import exc |
||||
from labertasche.database import labertasche_db as db |
||||
from labertasche.helper import is_valid_json, default_timestamp, check_gravatar, export_location |
||||
from labertasche.mail import mail |
||||
from labertasche.models import TComments, TLocation, TEmail |
||||
from labertasche.settings import Settings |
||||
from secrets import compare_digest |
||||
|
||||
|
||||
# Blueprint |
||||
bp_comments = Blueprint("bp_comments", __name__, url_prefix='/comments') |
||||
|
||||
|
||||
# Route for adding new comments |
||||
@bp_comments.route("/new", methods=['POST']) |
||||
@cross_origin() |
||||
def check_and_insert_new_comment(): |
||||
if request.method == 'POST': |
||||
settings = Settings() |
||||
smileys = settings.smileys |
||||
addons = settings.addons |
||||
sender = mail() |
||||
|
||||
# Check length of content and abort if too long or too short |
||||
if request.content_length > 2048: |
||||
return make_response(jsonify(status="post-max-length"), 400) |
||||
if request.content_length <= 0: |
||||
return make_response(jsonify(status="post-min-length"), 400) |
||||
|
||||
# get json from request |
||||
new_comment = request.json |
||||
|
||||
# save and sanitize location, nice try, bitch |
||||
location = new_comment['location'].strip().replace('.', '') |
||||
|
||||
# Validate json and check length again |
||||
if not is_valid_json(new_comment) or \ |
||||
len(new_comment['content']) < 40 or \ |
||||
len(new_comment['email']) < 5: |
||||
print("too short", file=stderr) |
||||
return make_response(jsonify(status='post-invalid-json'), 400) |
||||
|
||||
# Strip any HTML from message body |
||||
tags = re.compile('<.*?>') |
||||
special = re.compile('[&].*[;]') |
||||
content = re.sub(tags, '', new_comment['content']).strip() |
||||
content = re.sub(special, '', content).strip() |
||||
|
||||
# Convert smileys |
||||
if addons['smileys']: |
||||
for key, value in smileys.items(): |
||||
content = content.replace(key, value) |
||||
|
||||
# Update values |
||||
new_comment.update({"content": content}) |
||||
new_comment.update({"email": new_comment['email'].strip()}) |
||||
new_comment.update({"location": location}) |
||||
new_comment.update({"replied_to": None}) # Not (yet?) implemented |
||||
|
||||
# Check mail |
||||
if not sender.validate(new_comment['email']): |
||||
return make_response(jsonify(status='post-invalid-email'), 400) |
||||
|
||||
# check for spam |
||||
is_spam = spam(new_comment['content']) |
||||
has_score = score(new_comment['content']) |
||||
|
||||
# Insert mail into spam if detected, allow if listed as such |
||||
email = db.session.query(TEmail).filter(TEmail.email == new_comment['email']).first() |
||||
if not email: |
||||
if is_spam: |
||||
entry = { |
||||
"email": new_comment['email'], |
||||
"is_blocked": True, |
||||
"is_allowed": False |
||||
} |
||||
db.session.add(TEmail(**entry)) |
||||
if email: |
||||
if not email.is_allowed: |
||||
is_spam = True |
||||
if email.is_allowed: |
||||
is_spam = False |
||||
|
||||
# Look for location |
||||
loc_query = db.session.query(TLocation)\ |
||||
.filter(TLocation.location == new_comment['location']) |
||||
|
||||
if loc_query.first(): |
||||
# Set existing location id |
||||
new_comment.update({'location_id': loc_query.first().id_location}) |
||||
# TComments does not have this field |
||||
new_comment.pop("location") |
||||
else: |
||||
# Insert new location |
||||
loc_table = { |
||||
'location': new_comment['location'] |
||||
} |
||||
new_loc = TLocation(**loc_table) |
||||
db.session.add(new_loc) |
||||
db.session.flush() |
||||
db.session.refresh(new_loc) |
||||
new_comment.update({'location_id': new_loc.id_location}) |
||||
|
||||
# TComments does not have this field |
||||
new_comment.pop("location") |
||||
|
||||
# insert comment |
||||
try: |
||||
new_comment.update({"is_published": False}) |
||||
new_comment.update({"created_on": default_timestamp()}) |
||||
new_comment.update({"is_spam": is_spam}) |
||||
new_comment.update({"spam_score": has_score}) |
||||
new_comment.update({"gravatar": check_gravatar(new_comment['email'])}) |
||||
t_comment = TComments(**new_comment) |
||||
db.session.add(t_comment) |
||||
db.session.commit() |
||||
db.session.flush() |
||||
db.session.refresh(t_comment) |
||||
|
||||
# Send confirmation link and store returned value |
||||
hashes = sender.send_confirmation_link(new_comment['email']) |
||||
setattr(t_comment, "confirmation", hashes[0]) |
||||
setattr(t_comment, "deletion", hashes[1]) |
||||
db.session.commit() |
||||
|
||||
except exc.IntegrityError as e: |
||||
# Comment body exists, because content is unique |
||||
print(f"Duplicate from {request.environ['REMOTE_ADDR']}, error is:\n{e}", file=stderr) |
||||
return make_response(jsonify(status="post-duplicate"), 400) |
||||
|
||||
except Exception as e: # must be at bottom |
||||
# mail(f"check_and_insert_new_comment has thrown an error: {e}", ) |
||||
print(e, file=stderr) |
||||
return make_response(jsonify(status="post-internal-server-error"), 400) |
||||
|
||||
export_location(location) |
||||
return make_response(jsonify(status="post-success", comment_id=t_comment.comments_id), 200) |
||||
|
||||
|
||||
# Route for confirming comments |
||||
@bp_comments.route("/confirm/<email_hash>", methods=['GET']) |
||||
@cross_origin() |
||||
def check_confirmation_link(email_hash): |
||||
settings = Settings() |
||||
comment = db.session.query(TComments).filter(TComments.confirmation == email_hash).first() |
||||
if comment: |
||||
location = db.session.query(TLocation).filter(TLocation.id_location == comment.location_id).first() |
||||
if compare_digest(comment.confirmation, email_hash): |
||||
comment.confirmation = None |
||||
if not comment.is_spam: |
||||
setattr(comment, "is_published", True) |
||||
db.session.commit() |
||||
url = f"{settings.system['blog_url']}{location.location}#comment_{comment.comments_id}" |
||||
export_location(location.location) |
||||
return redirect(url) |
||||
|
||||
return redirect(f"{settings.system['blog_url']}?cnf=true") |
||||
|
||||
|
||||
# Route for deleting comments |
||||
@bp_comments.route("/delete/<email_hash>", methods=['GET']) |
||||
@cross_origin() |
||||
def check_deletion_link(email_hash): |
||||
settings = Settings() |
||||
query = db.session.query(TComments).filter(TComments.deletion == email_hash) |
||||
comment = query.first() |
||||
if comment: |
||||
location = db.session.query(TLocation).filter(TLocation.id_location == comment.location_id).first() |
||||
if compare_digest(comment.deletion, email_hash): |
||||
query.delete() |
||||
db.session.commit() |
||||
url = f"{settings.system['blog_url']}?deleted=true" |
||||
export_location(location.location) |
||||
return redirect(url) |
||||
|
||||
return redirect(f"{settings.system['blog_url']}?cnf=true") |
@ -0,0 +1,201 @@ |
||||
#!/usr/bin/env python3 |
||||
# -*- coding: utf-8 -*- |
||||
# /********************************************************************************** |
||||
# * _author : Domeniko Gentner |
||||
# * _mail : code@tuxstash.de |
||||
# * _repo : https://git.tuxstash.de/gothseidank/labertasche |
||||
# * _license : This project is under MIT License |
||||
# *********************************************************************************/ |
||||
from flask import Blueprint, render_template, request, redirect |
||||
from flask_login import login_required |
||||
from labertasche.database import labertasche_db as db |
||||
from labertasche.models import TLocation, TComments, TEmail |
||||
from labertasche.helper import dates_of_the_week |
||||
from sqlalchemy import func |
||||
import re |
||||
|
||||
# Blueprint |
||||
bp_dashboard = Blueprint("bp_dashboard", __name__, url_prefix='/dashboard') |
||||
|
||||
|
||||
@bp_dashboard.route('/') |
||||
@login_required |
||||
def dashboard_index(): |
||||
dates = dates_of_the_week() |
||||
spam = list() |
||||
published = list() |
||||
unpublished = list() |
||||
for each in dates: |
||||
spam_comments = db.session.query(TComments).filter(func.DATE(TComments.created_on) == each.date())\ |
||||
.filter(TComments.is_spam == True).all() |
||||
|
||||
pub_comments = db.session.query(TComments).filter(func.DATE(TComments.created_on) == each.date()) \ |
||||
.filter(TComments.is_spam == False)\ |
||||
.filter(TComments.is_published == True).all() |
||||
|
||||
unpub_comments = db.session.query(TComments).filter(func.DATE(TComments.created_on) == each.date()) \ |
||||
.filter(TComments.is_spam == False)\ |
||||
.filter(TComments.is_published == False).all() |
||||
|
||||
published.append(len(pub_comments)) |
||||
spam.append(len(spam_comments)) |
||||
unpublished.append(len(unpub_comments)) |
||||
|
||||
return render_template('dashboard.html', dates=dates, spam=spam, published=published, unpublished=unpublished) |
||||
|
||||
|
||||
@bp_dashboard.route('/review-spam/', methods=["POST", "GET"]) |
||||
@bp_dashboard.route('/review-spam/<int:location>', methods=["POST", "GET"]) |
||||
@login_required |
||||
def dashboard_review_spam(location=None): |
||||
all_locations = db.session.query(TLocation).all() |
||||
|
||||
# Check post |
||||
if request.method == "POST": |
||||
location = request.form.get('selected_location') |
||||
|
||||
# no parameters found |
||||
if location is None: |
||||
return render_template("review-spam.html", locations=all_locations, selected=location) |
||||
|
||||
try: |
||||
if int(location) >= 1: |
||||
spam_comments = db.session.query(TComments).filter(TComments.location_id == location)\ |
||||
.filter(TComments.is_spam == True) |
||||
return render_template("review-spam.html", locations=all_locations, selected=location, |
||||
spam_comments=spam_comments) |
||||
except ValueError: |
||||
pass |
||||
|
||||
return render_template("review-spam.html", locations=all_locations, selected=location) |
||||
|
||||
|
||||
@bp_dashboard.route('/manage-comments/', methods=["POST", "GET"]) |
||||
@bp_dashboard.route('/manage-comments/<int:location>', methods=["POST", "GET"]) |
||||
@login_required |
||||
def dashboard_manage_regular_comments(location=None): |
||||
all_locations = db.session.query(TLocation).all() |
||||
|
||||
# Check post |
||||
if request.method == "POST": |
||||
location = request.form.get('selected_location') |
||||
|
||||
# no parameters found |
||||
if location is None: |
||||
return render_template("manage-comments.html", locations=all_locations, selected=location) |
||||
|
||||
try: |
||||
if int(location) >= 1: |
||||
spam_comments = db.session.query(TComments).filter(TComments.location_id == location) \ |
||||
.filter(TComments.is_spam == False) |
||||
return render_template("manage-comments.html", locations=all_locations, selected=location, |
||||
spam_comments=spam_comments) |
||||
except ValueError: |
||||
pass |
||||
|
||||
return render_template("manage-comments.html", locations=all_locations, selected=location) |
||||
|
||||
|
||||
@bp_dashboard.route('/manage-mail/') |
||||
@login_required |
||||
def dashboard_allow_email(): |
||||
addresses = db.session.query(TEmail).all() |
||||
return render_template("manage_mail_addresses.html", addresses=addresses) |
||||
|
||||
|
||||
@bp_dashboard.route('/toggle-mail-allowed/<int:id_email>') |
||||
@login_required |
||||
def dashboard_allow_email_toggle(id_email): |
||||
address = db.session.query(TEmail).filter(TEmail.id_email == id_email).first() |
||||
if address: |
||||
setattr(address, "is_allowed", (not address.is_allowed)) |
||||
setattr(address, "is_blocked", (not address.is_blocked)) |
||||
db.session.commit() |
||||
return redirect(request.referrer) |
||||
|
||||
|
||||
@bp_dashboard.route('/reset-mail-reputation/<int:id_email>') |
||||
@login_required |
||||
def dashboard_reset_mail_reputation(id_email): |
||||
db.session.query(TEmail).filter(TEmail.id_email == id_email).delete() |
||||
db.session.commit() |
||||
return redirect(request.referrer) |
||||
|
||||
|
||||
@bp_dashboard.route('/delete-comment/<int:location_id>/<int:comment_id>', methods=['GET']) |
||||
@login_required |
||||
def dashboard_review_spam_delete_comment(location_id, comment_id): |
||||
comment = db.session.query(TComments).filter(TComments.comments_id == comment_id).first() |
||||
db.session.delete(comment) |
||||
db.session.commit() |
||||
|
||||
# Remove after last slash, to keep the location but get rid of the comment id |
||||
url = re.match("^(.*[/])", request.referrer)[0] |
||||
return redirect(f"{url}/{location_id}") |
||||
|
||||
|
||||
@bp_dashboard.route('/allow-comment/<int:location_id>/<int:comment_id>', methods=['GET']) |
||||
@login_required |
||||
def dashboard_review_spam_allow_comment(comment_id, location_id): |
||||
comment = db.session.query(TComments).filter(TComments.comments_id == comment_id).first() |
||||
if comment: |
||||
setattr(comment, 'is_published', True) |
||||
setattr(comment, 'is_spam', False) |
||||
db.session.commit() |
||||
|
||||
url = re.match("^(.*[/])", request.referrer)[0] |
||||
return redirect(f"{url}/{location_id}") |
||||
|
||||
|
||||
@bp_dashboard.route('/block-mail/<int:location_id>/<int:comment_id>', methods=["GET"]) |
||||
@login_required |
||||
def dashboard_review_spam_block_mail(location_id, comment_id): |
||||
comment = db.session.query(TComments).filter(TComments.comments_id == comment_id).first() |
||||
if comment: |
||||
mail = db.session.query(TEmail).filter(TEmail.email == comment.email).first() |
||||
if mail: |
||||
setattr(mail, 'is_allowed', False) |
||||
setattr(mail, 'is_blocked', True) |
||||
else: |
||||
new_mail = { |
||||
"email": comment.first().email, |
||||
"is_allowed": False, |
||||
"is_blocked": True |
||||
} |
||||
db.session.add(TEmail(**new_mail)) |
||||
|
||||
# Delete all comments made by this mail address |
||||
db.session.query(TComments).filter(TComments.email == comment.email).delete() |
||||
db.session.commit() |
||||
|
||||
url = re.match("^(.*[/])", request.referrer)[0] |
||||
return redirect(f"{url}/{location_id}") |
||||
|
||||
|
||||
@bp_dashboard.route('/allow-user/<int:location_id>/<int:comment_id>', methods=["GET"]) |
||||
@login_required |
||||
def dashboard_review_spam_allow_user(location_id, comment_id): |
||||
comment = db.session.query(TComments).filter(TComments.comments_id == comment_id).first() |
||||
if comment: |
||||
mail = db.session.query(TEmail).filter(TEmail.email == comment.email).first() |
||||
if mail: |
||||
setattr(mail, 'is_allowed', True) |
||||
setattr(mail, 'is_blocked', False) |
||||
else: |
||||
new_mail = { |
||||
"email": comment.email, |
||||
"is_allowed": True, |
||||
"is_blocked": False |
||||
} |
||||
db.session.add(TEmail(**new_mail)) |
||||
|
||||
# Allow all comments made by this mail address |
||||
all_comments = db.session.query(TComments).filter(TComments.email == comment.email).all() |
||||
if all_comments: |
||||
for comment in all_comments: |
||||
setattr(comment, 'is_published', True) |
||||
setattr(comment, 'is_spam', False) |
||||
|
||||
db.session.commit() |
||||
url = re.match("^(.*[/])", request.referrer)[0] |
||||
return redirect(f"{url}/{location_id}") |
@ -0,0 +1,46 @@ |
||||
#!/usr/bin/env python3 |
||||
# -*- coding: utf-8 -*- |
||||
# /********************************************************************************** |
||||
# * _author : Domeniko Gentner |
||||
# * _mail : code@tuxstash.de |
||||
# * _repo : https://git.tuxstash.de/gothseidank/labertasche |
||||
# * _license : This project is under MIT License |
||||
# *********************************************************************************/ |
||||
from flask import Blueprint, render_template, request, redirect, url_for |
||||
from flask_cors import cross_origin |
||||
from labertasche.helper import check_auth, User |
||||
from flask_login import login_user, current_user, logout_user |
||||
|
||||
# Blueprint |
||||
bp_login = Blueprint("bp_login", __name__) |
||||
|
||||
|
||||
@cross_origin() |
||||
@bp_login.route('/', methods=['GET']) |
||||
def show_login(): |
||||
if current_user.is_authenticated: |
||||
return redirect(url_for('bp_dashboard.dashboard_index')) |
||||
return render_template('login.html') |
||||
|
||||
|
||||
@cross_origin() |
||||
@bp_login.route('/login', methods=['POST', 'GET']) |
||||
def login(): |
||||
if request.method == 'POST': |
||||
username = request.form['username'] |
||||
password = request.form['password'] |
||||
|
||||
if check_auth(username, password): |
||||
login_user(User(0), remember=True) |
||||
return redirect(url_for('bp_dashboard.dashboard_index')) |
||||
|
||||
# Redirect get request to the login page |
||||
return redirect(url_for('bp_login.show_login')) |
||||
|
||||
|
||||
@cross_origin() |
||||
@bp_login.route('/logout/', methods=["GET"]) |
||||
def logout(): |
||||
if current_user.is_authenticated: |
||||
logout_user() |
||||
return redirect(url_for("bp_login.show_login")) |
@ -0,0 +1,12 @@ |
||||
#!/usr/bin/env python3 |
||||
# -*- coding: utf-8 -*- |
||||
# /********************************************************************************** |
||||
# * _author : Domeniko Gentner |
||||
# * _mail : code@tuxstash.de |
||||
# * _repo : https://git.tuxstash.de/gothseidank/labertasche |
||||
# * _license : This project is under MIT License |
||||
# *********************************************************************************/ |
||||
from flask_sqlalchemy import SQLAlchemy |
||||
|
||||
# Create SQLAlchemy |
||||
labertasche_db = SQLAlchemy() |
@ -0,0 +1,190 @@ |
||||
#!/usr/bin/env python3 |
||||
# -*- coding: utf-8 -*- |
||||
# /********************************************************************************** |
||||
# * _author : Domeniko Gentner |
||||
# * _mail : code@tuxstash.de |
||||
# * _repo : https://git.tuxstash.de/gothseidank/labertasche |
||||
# * _license : This project is under MIT License |
||||
# *********************************************************************************/ |
||||
import datetime |
||||
import json |
||||
from labertasche.models import TLocation, TComments |
||||
from labertasche.settings import Settings |
||||
from labertasche.database import labertasche_db as db |
||||
from functools import wraps |
||||
from hashlib import md5 |
||||
from flask import request |
||||
from flask_login import UserMixin |
||||
from secrets import compare_digest |
||||
from pathlib import Path |
||||
from sys import stderr |
||||
from re import match as re_match |
||||
import requests |
||||
|
||||
|
||||
class User(UserMixin): |
||||
def __init__(self, user_id): |
||||
self.id = user_id |
||||
|
||||
|
||||
def is_valid_json(j): |
||||
""" |
||||
Tries to load the json to test if it is valid. |
||||
|
||||
:param j: The json to test. |
||||
:return: True if the json is valid, False on any exception. |
||||
""" |
||||
try: |
||||
json.dumps(j) |
||||
return True |
||||
except json.JSONDecodeError as e: |
||||
print("not valid json") |
||||
return False |
||||
|
||||
|
||||
def default_timestamp(): |
||||
"""Timestamp used by the project to ensure consistency""" |
||||
date = datetime.datetime.now().replace(microsecond=0) |
||||
return date |
||||
|
||||
|
||||
def time_to_js(obj): |
||||
"""" |
||||
Returns a timestring readable by Javascript |
||||
""" |
||||
if isinstance(obj, (datetime.date, datetime.datetime)): |
||||
return obj.isoformat() |
||||
|
||||
|
||||
def alchemy_query_to_dict(obj): |
||||
""" |
||||
Used when exporting the data. It truncates the mail, removes the T from the date string, etc. |
||||
|
||||
:param obj: A single query item from sqlalchemy. |
||||
:return: a dict with the query |
||||
""" |
||||
no_mail = re_match("^.*[@]", obj.email)[0] |
||||
result = { |
||||
"comment_id": obj.comments_id, |
||||
"email": no_mail, |
||||
"content": obj.content, |
||||
"created_on": time_to_js(obj.created_on).replace("T", " "), |
||||
"replied_to": obj.replied_to, |
||||
"gravatar": obj.gravatar |
||||
} |
||||
return dict(result) |
||||
|
||||
|
||||
# Come on, it's a mail hash, don't complain |
||||
# noinspection InsecureHash |
||||
def check_gravatar(email: str): |
||||
""" |
||||
Builds the gravatar email hash, which uses md5. |
||||
You may use ?size=128 for example to dictate size in the final template. |
||||
:param email: the email to use for the hash |
||||
:return: the gravatar url of the image |
||||
""" |
||||
settings = Settings() |
||||
options = settings.gravatar |
||||
gravatar_hash = md5(email.strip().lower().encode("utf8")).hexdigest() |
||||
if options['cache']: |
||||
url = f"https://www.gravatar.com/avatar/{gravatar_hash}?s={options['size']}" |
||||
response = requests.get(url) |
||||
if response.ok: |
||||
outfile = Path(f"{options['static_dir']}/{gravatar_hash}.jpg") |
||||
if not outfile.exists(): |
||||
with outfile.open('wb') as fp: |
||||
response.raw.decode_content = True |
||||
for chunk in response: |
||||
fp.write(chunk) |
||||
|
||||
return gravatar_hash |
||||
|
||||
|
||||
def check_auth(username: str, password: str): |
||||
""" |
||||
Compares username and password from the settings file in a safe way. |
||||
Direct string comparison is vulnerable to timing attacks |
||||
https://sqreen.github.io/DevelopersSecurityBestPractices/timing-attack/python |
||||
:param username: username entered by the user |
||||
:param password: password entered by the user |
||||
:return: True if equal, False if not |
||||
""" |
||||
settings = Settings() |
||||
if compare_digest(username, settings.dashboard['username']) and \ |
||||
compare_digest(password, settings.dashboard['password']): |
||||
return True |
||||
return False |
||||
|
||||
|
||||
def basic_login_required(f): |
||||
""" |
||||
Decorator for basic auth |
||||
""" |
||||
@wraps(f) |
||||
def wrapped_view(**kwargs): |
||||
auth = request.authorization |
||||
if not (auth and check_auth(auth.username, auth.password)): |
||||
return ('Unauthorized', 401, { |
||||
'WWW-Authenticate': 'Basic realm="Login Required"' |
||||
}) |
||||
return f(**kwargs) |
||||
return wrapped_view |
||||
|
||||
|
||||
def export_location(location: str) -> bool: |
||||
""" |
||||
Exports the comments for the location after the comment was accepted |
||||
:param location: relative url of the hugo page |
||||
""" |
||||
try: |
||||
# Query |
||||
loc_query = db.session.query(TLocation).filter(TLocation.location == location).first() |
||||
|
||||
if loc_query: |
||||
comments = db.session.query(TComments).filter(TComments.is_spam != True) \ |
||||
.filter(TComments.is_published == True) \ |
||||
.filter(TComments.location_id == loc_query.id_location) \ |
||||
.filter(TComments.replied_to == None) |
||||
|
||||
bundle = { |
||||
"comments": [] |
||||
} |
||||
for comment in comments: |
||||
bundle['comments'].append(alchemy_query_to_dict(comment)) |
||||
|
||||
path_loc = re_match(".*(?=/)", loc_query.location)[0] |
||||
|
||||
system = Settings().system |
||||
out = Path(f"{system['output']}/{path_loc}.json") |
||||
out = out.absolute() |
||||
print(out) |
||||
folder = out.parents[0] |
||||
folder.mkdir(parents=True, exist_ok=True) |
||||
with out.open('w') as fp: |
||||
json.dump(bundle, fp) |
||||
|
||||
return True |
||||
|
||||
except Exception as e: |
||||
# mail(f"export_comments has thrown an error: {str(e)}") |
||||
print(e, file=stderr) |
||||
return False |
||||
|
||||
|
||||
def dates_of_the_week(): |
||||
""" |
||||
Finds all dates of this week and returns them as a list, |
||||
going from midnight on monday to sunday 1 second before midnight |
||||
:return: A list containing the dates |
||||
""" |
||||
date_list = list() |
||||
now = datetime.datetime.now() |
||||
monday = now - datetime.timedelta(days=now.weekday(), hours=now.hour, minutes=now.minute, seconds=now.second, |
||||
microseconds=now.microsecond) |
||||
date_list.append(monday) |
||||
for each in range(1, 6): |
||||
monday = monday + datetime.timedelta(days=1) |
||||
date_list.append(monday) |
||||
date_list.append((monday + datetime.timedelta(days=1, hours=23, minutes=59, seconds=59))) |
||||
return date_list |
@ -0,0 +1,100 @@ |
||||
#!/usr/bin/env python3 |
||||
# -*- coding: utf-8 -*- |
||||
# /********************************************************************************** |
||||
# * _author : Domeniko Gentner |
||||
# * _mail : code@tuxstash.de |
||||
# * _repo : https://git.tuxstash.de/gothseidank/labertasche |
||||
# * _license : This project is under MIT License |
||||
# *********************************************************************************/ |
||||
from email.mime.text import MIMEText |
||||
from email.mime.multipart import MIMEMultipart |
||||
from json import load as j_load |
||||
from pathlib import Path |
||||
from platform import system |
||||
from smtplib import SMTP_SSL, SMTPHeloError, SMTPAuthenticationError, SMTPException |
||||
from ssl import create_default_context |
||||
from labertasche.settings import Settings |
||||
from validate_email import validate_email |
||||
from secrets import token_urlsafe |
||||
|
||||
|
||||
class mail: |
||||
|
||||
def __init__(self): |
||||
path = Path("/etc/labertasche/mail_credentials.json") |
||||
if system().lower() == "windows": |
||||
path = Path("mail_credentials.json") |
||||
|
||||
with path.open("r") as fp: |
||||
self.credentials = j_load(fp) |
||||
|
||||
def send(self, txt_what: str, html_what: str, to: str): |
||||
if not self.credentials['enable']: |
||||
return |
||||
|
||||
txtmail = MIMEText(txt_what, "plain", _charset='utf8') |
||||
|
||||
multimime = MIMEMultipart('alternative') |
||||
multimime['Subject'] = "Comment confirmation pending" |
||||
multimime['From'] = self.credentials['email-sendfrom'] |
||||
multimime['To'] = to |
||||
multimime.attach(txtmail) |
||||
|
||||
# Only send HTML if needed |
||||
if html_what is not None: |
||||
htmlmail = MIMEText(html_what, "html", _charset='utf8') |
||||
multimime.attach(htmlmail) |
||||
|
||||
try: |
||||
with SMTP_SSL(host=self.credentials['smtp-server'], |
||||
port=self.credentials['smtp-port'], |
||||
context=create_default_context()) as server: |
||||
server.login(user=self.credentials['email-user'], password=self.credentials['email-password']) |
||||
server.sendmail(to_addrs=to, |
||||
msg=multimime.as_string(), |
||||
from_addr=self.credentials['email-sendfrom']) |
||||
|
||||
except SMTPHeloError as helo: |
||||
print(f"SMTPHeloError: {helo}") |
||||
except SMTPAuthenticationError as auth_error: |
||||
print(f"Authentication Error: {auth_error}") |
||||
except SMTPException as e: |
||||
print(f"SMTPException: {e}") |
||||
|
||||
def send_confirmation_link(self, email): |
||||
""" |
||||
Send confirmation link after entering a comment |
||||
:param email: The address to send the mail to |
||||
:return: A tuple with the confirmation token and the deletion token, in this order |
||||
""" |
||||
settings = Settings() |
||||
confirm_digest = token_urlsafe(48) |
||||
delete_digest = token_urlsafe(48) |
||||
|
||||
confirm_url = f"{settings.system['web_url']}/comments/confirm/{confirm_digest}" |
||||
delete_url = f"{settings.system['web_url']}/comments/delete/{delete_digest}" |
||||
|
||||
txt_what = f"Hey there. You have made a comment on {settings.system['blog_url']}. Please confirm it by " \ |
||||
f"copying this link into your browser:\n{confirm_url}\nIf you want to delete your comment for,"\ |
||||
f"whatever reason, please use this link:\n{delete_url}" |
||||
|
||||
html_what = f"Hey there. You have made a comment on {settings.system['blog_url']}.<br>Please confirm it by " \ |
||||
f"clicking on this <a href='{confirm_url}'>link</a>.<br>"\ |
||||
f"In case you want to delete your comment later, please click <a href='{delete_url}'>here</a>."\ |
||||
f"<br><br>If you think this is in error or someone made this comment in your name, please "\ |
||||
f"write me a <a href='mailto:contact@tuxstash.de'>mail</a> to discuss options such as " \ |
||||
f"blocking your mail from being used." |
||||
|
||||
self.send(txt_what, html_what, email) |
||||
|
||||
return confirm_digest, delete_digest |
||||
|
||||
def validate(self, addr): |
||||
# validate email |
||||
is_valid = validate_email(email_address=addr, |
||||
check_regex=True, |
||||
check_mx=False, |
||||
dns_timeout=10, |
||||
use_blacklist=True, |
||||
debug=False) |
||||
return is_valid |
@ -0,0 +1,11 @@ |
||||
#!/usr/bin/env python3 |
||||
# -*- coding: utf-8 -*- |
||||
# /********************************************************************************** |
||||
# * _author : Domeniko Gentner |
||||
# * _mail : code@tuxstash.de |
||||
# * _repo : https://git.tuxstash.de/gothseidank/labertasche |
||||
# * _license : This project is under MIT License |
||||
# *********************************************************************************/ |
||||
from .t_comments import TComments |
||||
from .t_location import TLocation |
||||
from .t_emails import TEmail |
@ -0,0 +1,34 @@ |
||||
#!/usr/bin/env python3 |
||||
# -*- coding: utf-8 -*- |
||||
# /********************************************************************************** |
||||
# * _author : Domeniko Gentner |
||||
# * _mail : code@tuxstash.de |
||||
# * _repo : https://git.tuxstash.de/gothseidank/labertasche |
||||
# * _license : This project is under MIT License |
||||
# *********************************************************************************/ |
||||
from labertasche.database import labertasche_db as db |
||||
from sqlalchemy import ForeignKey |
||||
|
||||
|
||||
class TComments(db.Model): |
||||
# table name |
||||
__tablename__ = "t_comments" |
||||
__table_args__ = {'useexisting': True} |
||||
|
||||
# primary key |
||||
comments_id = db.Column(db.Integer, primary_key=True) |
||||
|
||||
# foreign keys |
||||
location_id = db.Column(db.Text, ForeignKey('t_location.id_location'), nullable=False) |
||||
|
||||
# data |
||||
email = db.Column(db.Text, nullable=False) |
||||
content = db.Column(db.Text, nullable=False, unique=True) |
||||
created_on = db.Column(db.DateTime, nullable=False) |
||||
is_published = db.Column(db.Boolean, nullable=False) |
||||
is_spam = db.Column(db.Boolean, nullable=False) |
||||
spam_score = db.Column(db.Float, nullable=False) |
||||
replied_to = db.Column(db.Boolean, nullable=True) |
||||
confirmation = db.Column(db.Text, nullable=True) |
||||
deletion = db.Column(db.Text, nullable=True) |
||||
gravatar = db.Column(db.Text, nullable=True) |
@ -0,0 +1,23 @@ |
||||
#!/usr/bin/env python3 |
||||
# -*- coding: utf-8 -*- |
||||
# /********************************************************************************** |
||||
# * _author : Domeniko Gentner |
||||
# * _mail : code@tuxstash.de |
||||
# * _repo : https://git.tuxstash.de/gothseidank/labertasche |
||||
# * _license : This project is under MIT License |
||||
# *********************************************************************************/ |
||||
from labertasche.database import labertasche_db as db |
||||
|
||||
|
||||
class TEmail(db.Model): |
||||
# Table name |
||||
__tablename__ = 't_email' |
||||
__table_args__ = {'useexisting': True} |
||||
|
||||
# primary key |
||||
id_email = db.Column(db.Integer, primary_key=True) |
||||
|
||||
# data |
||||
email = db.Column(db.Integer, unique=True) |
||||
is_blocked = db.Column(db.Boolean) |
||||
is_allowed = db.Column(db.Boolean) |
@ -0,0 +1,21 @@ |
||||
#!/usr/bin/env python3 |
||||
# -*- coding: utf-8 -*- |
||||
# /********************************************************************************** |
||||
# * _author : Domeniko Gentner |
||||
# * _mail : code@tuxstash.de |
||||
# * _repo : https://git.tuxstash.de/gothseidank/labertasche |
||||
# * _license : This project is under MIT License |
||||
# *********************************************************************************/ |
||||
from labertasche.database import labertasche_db as db |
||||
|
||||
|
||||
class TLocation(db.Model): |
||||
# table name |
||||
__tablename__ = "t_location" |
||||
__table_args__ = {'useexisting': True} |
||||
|
||||
# primary key |
||||
id_location = db.Column(db.Integer, primary_key=True) |
||||
|
||||
# data |
||||
location = db.Column(db.Text, nullable=False, unique=True) |
@ -0,0 +1,30 @@ |
||||
#!/usr/bin/env python3 |
||||
# -*- coding: utf-8 -*- |
||||
# /********************************************************************************** |
||||
# * _author : Domeniko Gentner |
||||
# * _mail : code@tuxstash.de |
||||
# * _repo : https://git.tuxstash.de/gothseidank/labertasche |
||||
# * _license : This project is under MIT License |
||||
# *********************************************************************************/ |
||||
import yaml |
||||
from pathlib import Path |
||||
from platform import system |
||||
|
||||
|
||||
class Settings: |
||||
""" |
||||
Automatically loads the settings from /etc/ on Linux and same directory on other OS |
||||
""" |
||||
def __init__(self): |
||||
file = Path("labertasche.yaml") |
||||
if system().lower() == "linux": |
||||
file = Path("/etc/labertasche/labertasche.yaml") |
||||
|
||||
with file.open('r') as fp: |
||||
conf = yaml.safe_load(fp) |
||||
|
||||
self.system = conf['system'] |
||||
self.dashboard = conf['dashboard'] |
||||
self.gravatar = conf['gravatar'] |
||||
self.addons = conf['addons'] |
||||
self.smileys = conf['smileys'] |
@ -0,0 +1,9 @@ |
||||
{ |
||||
"enable": true, |
||||
"smtp-server": "mail server", |
||||
"smtp-port": 465, |
||||
"email-user": "username for smtp", |
||||
"email-sendfrom": "sender mail", |
||||
"email-password": "passw0rd", |
||||
"email-sendto": "receiving mail" |
||||
} |
@ -0,0 +1,75 @@ |
||||
#!/usr/bin/env python3 |
||||
# -*- coding: utf-8 -*- |
||||
# /********************************************************************************** |
||||
# * _author : Domeniko Gentner |
||||
# * _mail : code@tuxstash.de |
||||
# * _repo : https://git.tuxstash.de/gothseidank/labertasche |
||||
# * _license : This project is under MIT License |
||||
# *********************************************************************************/ |
||||
import logging |
||||
from flask import Flask, redirect, url_for |
||||
from flask_cors import CORS |
||||
from sqlalchemy import event |
||||
# noinspection PyProtectedMember |
||||
from sqlalchemy.engine import Engine |
||||
from labertasche.settings import Settings |
||||
from labertasche.database import labertasche_db |
||||
from labertasche.blueprints import bp_comments, bp_login, bp_dashboard |
||||
from labertasche.helper import User |
||||
from flask_login import LoginManager |
||||
|
||||
|
||||
# Load settings |
||||
settings = Settings() |
||||
|
||||
# Flask App |
||||
laberflask = Flask(__name__) |
||||
laberflask.config.update(dict( |
||||
SESSION_COOKIE_DOMAIN=settings.system['cookie-domain'], |
||||
DEBUG=settings.system['debug'], |
||||
SECRET_KEY=settings.system['secret'], |
||||
TEMPLATES_AUTO_RELOAD=True, |
||||
SQLALCHEMY_DATABASE_URI=settings.system['database_uri'], |
||||
SQLALCHEMY_TRACK_MODIFICATIONS=False |
||||
)) |
||||
|
||||
# CORS |
||||
CORS(laberflask, resources={r"/comments": {"origins": settings.system['blog_url']}}) |
||||
|
||||
# Import blueprints |
||||
laberflask.register_blueprint(bp_comments) |
||||
laberflask.register_blueprint(bp_dashboard) |
||||
laberflask.register_blueprint(bp_login) |
||||
|
||||
# Disable Werkzeug's verbosity during development |
||||
log = logging.getLogger('werkzeug') |
||||
log.setLevel(logging.ERROR) |
||||
|
||||
# Initialize ORM |
||||
labertasche_db.init_app(laberflask) |
||||
with laberflask.app_context(): |
||||
labertasche_db.create_all() |
||||
|
||||
# Set up login manager |
||||
loginmgr = LoginManager(laberflask) |
||||
loginmgr.login_view = 'bp_admin_login.login' |
||||
|
||||
|
||||
@loginmgr.user_loader |
||||
def user_loader(user_id): |
||||
if user_id != "0": |
||||
return None |
||||
return User(user_id) |
||||
|
||||
|
||||
@loginmgr.unauthorized_handler |
||||
def login_invalid(): |
||||
return redirect(url_for('bp_login.show_login')) |
||||
|
||||
|
||||
@event.listens_for(Engine, "connect") |
||||
def set_sqlite_pragma(dbapi_connection, connection_record): |
||||
if settings.system["database_uri"][0:6] == 'sqlite': |
||||
cursor = dbapi_connection.cursor() |
||||
cursor.execute("PRAGMA journal_mode=WAL;") |
||||
cursor.close() |
@ -0,0 +1,16 @@ |
||||
#!/usr/bin/env python3 |
||||
# -*- coding: utf-8 -*- |
||||
# """ |
||||
# /********************************************************************************** |
||||
# * _author : Domeniko Gentner |
||||
# * _mail : code@tuxstash.de |
||||
# * _repo : https://git.tuxstash.de/gothseidank/piradio |
||||
# * _license : This project is under GPL.v2 |
||||
# *********************************************************************************/ |
||||
from sys import path as sys_path |
||||
from os import path as os_path |
||||
|
||||
sys_path.insert(0, os_path.dirname(os_path.realpath(__file__))) |
||||
|
||||
from server import laberflask |
||||
application = laberflask |
@ -0,0 +1 @@ |
||||
@keyframes chartjs-render-animation{from{opacity:.99}to{opacity:1}}.chartjs-render-monitor{animation:chartjs-render-animation 1ms}.chartjs-size-monitor,.chartjs-size-monitor-expand,.chartjs-size-monitor-shrink{position:absolute;direction:ltr;left:0;top:0;right:0;bottom:0;overflow:hidden;pointer-events:none;visibility:hidden;z-index:-1}.chartjs-size-monitor-expand>div{position:absolute;width:1000000px;height:1000000px;left:0;top:0}.chartjs-size-monitor-shrink>div{position:absolute;width:200%;height:200%;left:0;top:0} |
File diff suppressed because it is too large
Load Diff
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@ -0,0 +1,5 @@ |
||||
/* Bulma Base */ |
||||
@charset "utf-8" |
||||
|
||||
@import "minireset.sass" |
||||
@import "generic.sass" |
@ -0,0 +1,143 @@ |
||||
$body-background-color: $scheme-main !default |
||||
$body-size: 16px !default |
||||
$body-min-width: 300px !default |
||||
$body-rendering: optimizeLegibility !default |
||||
$body-family: $family-primary !default |
||||
$body-overflow-x: hidden !default |
||||
$body-overflow-y: scroll !default |
||||
|
||||
$body-color: $text !default |
||||
$body-font-size: 1em !default |
||||
$body-weight: $weight-normal !default |
||||
$body-line-height: 1.5 !default |
||||
|
||||
$code-family: $family-code !default |
||||
$code-padding: 0.25em 0.5em 0.25em !default |
||||
$code-weight: normal !default |
||||
$code-size: 0.875em !default |
||||
|
||||
$small-font-size: 0.875em !default |
||||
|
||||
$hr-background-color: $background !default |
||||
$hr-height: 2px !default |
||||
$hr-margin: 1.5rem 0 !default |
||||
|
||||
$strong-color: $text-strong !default |
||||
$strong-weight: $weight-bold !default |
||||
|
||||
$pre-font-size: 0.875em !default |
||||
$pre-padding: 1.25rem 1.5rem !default |
||||
$pre-code-font-size: 1em !default |
||||
|
||||
html |
||||
background-color: $body-background-color |
||||
font-size: $body-size |
||||
-moz-osx-font-smoothing: grayscale |
||||
-webkit-font-smoothing: antialiased |
||||
min-width: $body-min-width |
||||
overflow-x: $body-overflow-x |
||||
overflow-y: $body-overflow-y |
||||
text-rendering: $body-rendering |
||||
text-size-adjust: 100% |
||||
|
||||
article, |
||||
aside, |
||||
figure, |
||||
footer, |
||||
header, |
||||
hgroup, |
||||
section |
||||
display: block |
||||
|
||||
body, |
||||
button, |
||||
input, |
||||
optgroup, |
||||
select, |
||||
textarea |
||||
font-family: $body-family |
||||
|
||||
code, |
||||
pre |
||||
-moz-osx-font-smoothing: auto |
||||
-webkit-font-smoothing: auto |
||||
font-family: $code-family |
||||
|
||||
body |
||||
color: $body-color |
||||
font-size: $body-font-size |
||||
font-weight: $body-weight |
||||
line-height: $body-line-height |
||||
|
||||
// Inline |
||||
|
||||
a |
||||
color: $link |
||||
cursor: pointer |
||||
text-decoration: none |
||||
strong |
||||
color: currentColor |
||||
&:hover |
||||
color: $link-hover |
||||
|
||||
code |
||||
background-color: $code-background |
||||
color: $code |
||||
font-size: $code-size |
||||
font-weight: $code-weight |
||||
padding: $code-padding |
||||
|
||||
hr |
||||
background-color: $hr-background-color |
||||
border: none |
||||
display: block |
||||
height: $hr-height |
||||
margin: $hr-margin |
||||
|
||||
img |
||||
height: auto |
||||
max-width: 100% |
||||
|
||||
input[type="checkbox"], |
||||
input[type="radio"] |
||||
vertical-align: baseline |
||||
|
||||
small |
||||
font-size: $small-font-size |
||||
|
||||
span |
||||
font-style: inherit |
||||
font-weight: inherit |
||||
|
||||
strong |
||||
color: $strong-color |
||||
font-weight: $strong-weight |
||||
|
||||
// Block |
||||
|
||||
fieldset |
||||
border: none |
||||
|
||||
pre |
||||
+overflow-touch |
||||
background-color: $pre-background |
||||
color: $pre |
||||
font-size: $pre-font-size |
||||
overflow-x: auto |
||||
padding: $pre-padding |
||||
white-space: pre |
||||
word-wrap: normal |
||||
code |
||||
background-color: transparent |
||||
color: currentColor |
||||
font-size: $pre-code-font-size |
||||
padding: 0 |
||||
|
||||
table |
||||
td, |
||||
th |
||||
vertical-align: top |
||||
&:not([align]) |
||||
text-align: inherit |
||||
th |
||||
color: $text-strong |
@ -0,0 +1 @@ |
||||
@warn "The helpers.sass file is DEPRECATED. It has moved into its own /helpers folder. Please import sass/helpers/_all instead." |
@ -0,0 +1,79 @@ |
||||
/*! minireset.css v0.0.6 | MIT License | github.com/jgthms/minireset.css */ |
||||
// Blocks |
||||
html, |
||||
body, |
||||
p, |
||||
ol, |
||||
ul, |
||||
li, |
||||
dl, |
||||
dt, |
||||
dd, |
||||
blockquote, |
||||
figure, |
||||
fieldset, |
||||
legend, |
||||
textarea, |
||||
pre, |
||||
iframe, |
||||
hr, |
||||
h1, |
||||
h2, |
||||
h3, |
||||
h4, |
||||
h5, |
||||
h6 |
||||
margin: 0 |
||||
padding: 0 |
||||
|
||||
// Headings |
||||
h1, |
||||
h2, |
||||
h3, |
||||
h4, |
||||
h5, |
||||
h6 |
||||
font-size: 100% |
||||
font-weight: normal |
||||
|
||||
// List |
||||
ul |
||||
list-style: none |
||||
|
||||
// Form |
||||
button, |
||||
input, |
||||
select, |
||||
textarea |
||||
margin: 0 |
||||
|
||||
// Box sizing |
||||
html |
||||
box-sizing: border-box |
||||
|
||||
* |
||||
&, |
||||
&::before, |
||||
&::after |
||||
box-sizing: inherit |
||||
|
||||
// Media |
||||
img, |
||||
video |
||||
height: auto |
||||
max-width: 100% |
||||
|
||||
// Iframe |
||||
iframe |
||||
border: 0 |
||||
|
||||
// Table |
||||
table |
||||
border-collapse: collapse |
||||
border-spacing: 0 |
||||
|
||||
td, |
||||
th |
||||
padding: 0 |
||||
&:not([align]) |
||||
text-align: inherit |
@ -0,0 +1,15 @@ |
||||
/* Bulma Components */ |
||||
@charset "utf-8" |
||||
|
||||
@import "breadcrumb.sass" |
||||
@import "card.sass" |
||||
@import "dropdown.sass" |
||||
@import "level.sass" |
||||
@import "media.sass" |
||||
@import "menu.sass" |
||||
@import "message.sass" |
||||
@import "modal.sass" |
||||
@import "navbar.sass" |
||||
@import "pagination.sass" |
||||
@import "panel.sass" |
||||
@import "tabs.sass" |
@ -0,0 +1,75 @@ |
||||
$breadcrumb-item-color: $link !default |
||||
$breadcrumb-item-hover-color: $link-hover !default |
||||
$breadcrumb-item-active-color: $text-strong !default |
||||
|
||||
$breadcrumb-item-padding-vertical: 0 !default |
||||
$breadcrumb-item-padding-horizontal: 0.75em !default |
||||
|
||||
$breadcrumb-item-separator-color: $border-hover !default |
||||
|
||||
.breadcrumb |
||||
@extend %block |
||||
@extend %unselectable |
||||
font-size: $size-normal |
||||
white-space: nowrap |
||||
a |
||||
align-items: center |
||||
color: $breadcrumb-item-color |
||||
display: flex |
||||
justify-content: center |
||||
padding: $breadcrumb-item-padding-vertical $breadcrumb-item-padding-horizontal |
||||
&:hover |
||||
color: $breadcrumb-item-hover-color |
||||
li |
||||
align-items: center |
||||
display: flex |
||||
&:first-child a |
||||
+ltr-property("padding", 0, false) |
||||
&.is-active |
||||
a |
||||
color: $breadcrumb-item-active-color |
||||
cursor: default |
||||
pointer-events: none |
||||
& + li::before |
||||
color: $breadcrumb-item-separator-color |
||||
content: "\0002f" |
||||
ul, |
||||
ol |
||||
align-items: flex-start |
||||
display: flex |
||||
flex-wrap: wrap |
||||
justify-content: flex-start |
||||
.icon |
||||
&:first-child |
||||
+ltr-property("margin", 0.5em) |
||||
&:last-child |
||||
+ltr-property("margin", 0.5em, false) |
||||
// Alignment |
||||
&.is-centered |
||||
ol, |
||||
ul |
||||
justify-content: center |
||||
&.is-right |
||||
ol, |
||||
ul |
||||
justify-content: flex-end |
||||
// Sizes |
||||
&.is-small |
||||
font-size: $size-small |
||||
&.is-medium |
||||
font-size: $size-medium |
||||
&.is-large |
||||
font-size: $size-large |
||||
// Styles |
||||
&.has-arrow-separator |
||||
li + li::before |
||||
content: "\02192" |
||||
&.has-bullet-separator |
||||
li + li::before |
||||
content: "\02022" |
||||
&.has-dot-separator |
||||
li + li::before |
||||
content: "\000b7" |
||||
&.has-succeeds-separator |
||||
li + li::before |
||||
content: "\0227B" |
@ -0,0 +1,83 @@ |
||||
$card-color: $text !default |
||||
$card-background-color: $scheme-main !default |
||||
$card-shadow: 0 0.5em 1em -0.125em rgba($scheme-invert, 0.1), 0 0px 0 1px rgba($scheme-invert, 0.02) !default |
||||
$card-radius: 0.25rem !default |
||||
$card-overflow: hidden !default |
||||
|
||||
$card-header-background-color: transparent !default |
||||
$card-header-color: $text-strong !default |
||||
$card-header-padding: 0.75rem 1rem !default |
||||
$card-header-shadow: 0 0.125em 0.25em rgba($scheme-invert, 0.1) !default |
||||
$card-header-weight: $weight-bold !default |
||||
|
||||
$card-content-background-color: transparent !default |
||||
$card-content-padding: 1.5rem !default |
||||
|
||||
$card-footer-background-color: transparent !default |
||||
$card-footer-border-top: 1px solid $border-light !default |
||||
$card-footer-padding: 0.75rem !default |
||||
|
||||
$card-media-margin: $block-spacing !default |
||||
|
||||
.card |
||||
background-color: $card-background-color |
||||
border-radius: $card-radius |
||||
box-shadow: $card-shadow |
||||
color: $card-color |
||||
max-width: 100% |
||||
overflow: $card-overflow |
||||
position: relative |
||||
|
||||
.card-header |
||||
background-color: $card-header-background-color |
||||
align-items: stretch |
||||
box-shadow: $card-header-shadow |
||||
display: flex |
||||
|
||||
.card-header-title |
||||
align-items: center |
||||
color: $card-header-color |
||||
display: flex |
||||
flex-grow: 1 |
||||
font-weight: $card-header-weight |
||||
padding: $card-header-padding |
||||
&.is-centered |
||||
justify-content: center |
||||
|
||||
.card-header-icon |
||||
align-items: center |
||||
cursor: pointer |
||||
display: flex |
||||
justify-content: center |
||||
padding: $card-header-padding |
||||
|
||||
.card-image |
||||
display: block |
||||
position: relative |
||||
|
||||
.card-content |
||||
background-color: $card-content-background-color |
||||
padding: $card-content-padding |
||||
|
||||
.card-footer |
||||
background-color: $card-footer-background-color |
||||
border-top: $card-footer-border-top |
||||
align-items: stretch |
||||
display: flex |
||||
|
||||
.card-footer-item |
||||
align-items: center |
||||
display: flex |
||||
flex-basis: 0 |
||||
flex-grow: 1 |
||||
flex-shrink: 0 |
||||
justify-content: center |
||||
padding: $card-footer-padding |
||||
&:not(:last-child) |
||||
+ltr-property("border", $card-footer-border-top) |
||||
|
||||
// Combinations |
||||
|
||||
.card |
||||
.media:not(:last-child) |
||||
margin-bottom: $card-media-margin |
@ -0,0 +1,81 @@ |
||||
$dropdown-menu-min-width: 12rem !default |
||||
|
||||
$dropdown-content-background-color: $scheme-main !default |
||||
$dropdown-content-arrow: $link !default |
||||
$dropdown-content-offset: 4px !default |
||||
$dropdown-content-padding-bottom: 0.5rem !default |
||||
$dropdown-content-padding-top: 0.5rem !default |
||||
$dropdown-content-radius: $radius !default |
||||
$dropdown-content-shadow: 0 0.5em 1em -0.125em rgba($scheme-invert, 0.1), 0 0px 0 1px rgba($scheme-invert, 0.02) !default |
||||
$dropdown-content-z: 20 !default |
||||
|
||||
$dropdown-item-color: $text !default |
||||
$dropdown-item-hover-color: $scheme-invert !default |
||||
$dropdown-item-hover-background-color: $background !default |
||||
$dropdown-item-active-color: $link-invert !default |
||||
$dropdown-item-active-background-color: $link !default |
||||
|
||||
$dropdown-divider-background-color: $border-light !default |
||||
|
||||
.dropdown |
||||
display: inline-flex |
||||
position: relative |
||||
vertical-align: top |
||||
&.is-active, |
||||
&.is-hoverable:hover |
||||
.dropdown-menu |
||||
display: block |
||||
&.is-right |
||||
.dropdown-menu |
||||
left: auto |
||||
right: 0 |
||||
&.is-up |
||||
.dropdown-menu |
||||
bottom: 100% |
||||
padding-bottom: $dropdown-content-offset |
||||
padding-top: initial |
||||
top: auto |
||||
|
||||
.dropdown-menu |
||||
display: none |
||||
+ltr-position(0, false) |
||||
min-width: $dropdown-menu-min-width |
||||
padding-top: $dropdown-content-offset |
||||
position: absolute |
||||
top: 100% |
||||
z-index: $dropdown-content-z |
||||
|
||||
.dropdown-content |
||||
background-color: $dropdown-content-background-color |
||||
border-radius: $dropdown-content-radius |
||||
box-shadow: $dropdown-content-shadow |
||||
padding-bottom: $dropdown-content-padding-bottom |
||||
padding-top: $dropdown-content-padding-top |
||||
|
||||
.dropdown-item |
||||
color: $dropdown-item-color |
||||
display: block |
||||
font-size: 0.875rem |
||||
line-height: 1.5 |
||||
padding: 0.375rem 1rem |
||||
position: relative |
||||
|
||||
a.dropdown-item, |
||||
button.dropdown-item |
||||
+ltr-property("padding", 3rem) |
||||
text-align: inherit |
||||
white-space: nowrap |
||||
width: 100% |
||||
&:hover |
||||
background-color: $dropdown-item-hover-background-color |
||||
color: $dropdown-item-hover-color |
||||
&.is-active |
||||
background-color: $dropdown-item-active-background-color |
||||
color: $dropdown-item-active-color |
||||
|
||||
.dropdown-divider |
||||
background-color: $dropdown-divider-background-color |
||||
border: none |
||||
display: block |
||||
height: 1px |
||||
margin: 0.5rem 0 |
@ -0,0 +1,77 @@ |
||||
$level-item-spacing: ($block-spacing / 2) !default |
||||
|
||||
.level |
||||
@extend %block |
||||
align-items: center |
||||
justify-content: space-between |
||||
code |
||||
border-radius: $radius |
||||
img |
||||
display: inline-block |
||||
vertical-align: top |
||||
// Modifiers |
||||
&.is-mobile |
||||
display: flex |
||||
.level-left, |
||||
.level-right |
||||
display: flex |
||||
.level-left + .level-right |
||||
margin-top: 0 |
||||
.level-item |
||||
&:not(:last-child) |
||||
margin-bottom: 0 |
||||
+ltr-property("margin", $level-item-spacing) |
||||
&:not(.is-narrow) |
||||
flex-grow: 1 |
||||
// Responsiveness |
||||
+tablet |
||||
display: flex |
||||
& > .level-item |
||||
&:not(.is-narrow) |
||||
flex-grow: 1 |
||||
|
||||
.level-item |
||||
align-items: center |
||||
display: flex |
||||
flex-basis: auto |
||||
flex-grow: 0 |
||||
flex-shrink: 0 |
||||
justify-content: center |
||||
.title, |
||||
.subtitle |
||||
margin-bottom: 0 |
||||
// Responsiveness |
||||
+mobile |
||||
&:not(:last-child) |
||||
margin-bottom: $level-item-spacing |
||||
|
||||
.level-left, |
||||
.level-right |
||||
flex-basis: auto |
||||
flex-grow: 0 |
||||
flex-shrink: 0 |
||||
.level-item |
||||
// Modifiers |
||||
&.is-flexible |
||||
flex-grow: 1 |
||||
// Responsiveness |
||||
+tablet |
||||
&:not(:last-child) |
||||
+ltr-property("margin", $level-item-spacing) |
||||
|
||||
.level-left |
||||
align-items: center |
||||
justify-content: flex-start |
||||
// Responsiveness |
||||
+mobile |
||||
& + .level-right |
||||
margin-top: 1.5rem |
||||
+tablet |
||||
display: flex |
||||
|
||||
.level-right |
||||
align-items: center |
||||
justify-content: flex-end |
||||
// Responsiveness |
||||
+tablet |
||||
display: flex |
@ -0,0 +1,52 @@ |
||||
$media-border-color: bulmaRgba($border, 0.5) !default |
||||
$media-spacing: 1rem |
||||
$media-spacing-large: 1.5rem |
||||
|
||||
.media |
||||
align-items: flex-start |
||||
display: flex |
||||
text-align: inherit |
||||
.content:not(:last-child) |
||||
margin-bottom: 0.75rem |
||||
.media |
||||
border-top: 1px solid $media-border-color |
||||
display: flex |
||||
padding-top: 0.75rem |
||||
.content:not(:last-child), |
||||
.control:not(:last-child) |
||||
margin-bottom: 0.5rem |
||||
.media |
||||
padding-top: 0.5rem |
||||
& + .media |
||||
margin-top: 0.5rem |
||||
& + .media |
||||
border-top: 1px solid $media-border-color |
||||
margin-top: $media-spacing |
||||
padding-top: $media-spacing |
||||
// Sizes |
||||
&.is-large |
||||
& + .media |
||||
margin-top: $media-spacing-large |
||||
padding-top: $media-spacing-large |
||||
|
||||
.media-left, |
||||
.media-right |
||||
flex-basis: auto |
||||
flex-grow: 0 |
||||
flex-shrink: 0 |
||||
|
||||
.media-left |
||||
+ltr-property("margin", $media-spacing) |
||||
|
||||
.media-right |
||||
+ltr-property("margin", $media-spacing, false) |
||||
|
||||
.media-content |
||||
flex-basis: auto |
||||
flex-grow: 1 |
||||
flex-shrink: 1 |
||||
text-align: inherit |
||||
|
||||
+mobile |
||||
.media-content |
||||
overflow-x: auto |
@ -0,0 +1,57 @@ |
||||
$menu-item-color: $text !default |
||||
$menu-item-radius: $radius-small !default |
||||
$menu-item-hover-color: $text-strong !default |
||||
$menu-item-hover-background-color: $background !default |
||||
$menu-item-active-color: $link-invert !default |
||||
$menu-item-active-background-color: $link !default |
||||
|
||||
$menu-list-border-left: 1px solid $border !default |
||||
$menu-list-line-height: 1.25 !default |
||||
$menu-list-link-padding: 0.5em 0.75em !default |
||||
$menu-nested-list-margin: 0.75em !default |
||||
$menu-nested-list-padding-left: 0.75em !default |
||||
|
||||
$menu-label-color: $text-light !default |
||||
$menu-label-font-size: 0.75em !default |
||||
$menu-label-letter-spacing: 0.1em !default |
||||
$menu-label-spacing: 1em !default |
||||
|
||||
.menu |
||||
font-size: $size-normal |
||||
// Sizes |
||||
&.is-small |
||||
font-size: $size-small |
||||
&.is-medium |
||||
font-size: $size-medium |
||||
&.is-large |
||||
font-size: $size-large |
||||
|
||||
.menu-list |
||||
line-height: $menu-list-line-height |
||||
a |
||||
border-radius: $menu-item-radius |
||||
color: $menu-item-color |
||||
display: block |
||||
padding: $menu-list-link-padding |
||||
&:hover |
||||
background-color: $menu-item-hover-background-color |
||||
color: $menu-item-hover-color |
||||
// Modifiers |
||||
&.is-active |
||||
background-color: $menu-item-active-background-color |
||||
color: $menu-item-active-color |
||||
li |
||||
ul |
||||
+ltr-property("border", $menu-list-border-left, false) |
||||
margin: $menu-nested-list-margin |
||||
+ltr-property("padding", $menu-nested-list-padding-left, false) |
||||
|
||||
.menu-label |
||||
color: $menu-label-color |
||||
font-size: $menu-label-font-size |
||||
letter-spacing: $menu-label-letter-spacing |
||||
text-transform: uppercase |
||||
&:not(:first-child) |
||||
margin-top: $menu-label-spacing |
||||
&:not(:last-child) |
||||
margin-bottom: $menu-label-spacing |
@ -0,0 +1,99 @@ |
||||
$message-background-color: $background !default |
||||
$message-radius: $radius !default |
||||
|
||||
$message-header-background-color: $text !default |
||||
$message-header-color: $text-invert !default |
||||
$message-header-weight: $weight-bold !default |
||||
$message-header-padding: 0.75em 1em !default |
||||
$message-header-radius: $radius !default |
||||
|
||||
$message-body-border-color: $border !default |
||||
$message-body-border-width: 0 0 0 4px !default |
||||
$message-body-color: $text !default |
||||
$message-body-padding: 1.25em 1.5em !default |
||||
$message-body-radius: $radius !default |
||||
|
||||
$message-body-pre-background-color: $scheme-main !default |
||||
$message-body-pre-code-background-color: transparent !default |
||||
|
||||
$message-header-body-border-width: 0 !default |
||||
$message-colors: $colors !default |
||||
|
||||
.message |
||||
@extend %block |
||||
background-color: $message-background-color |
||||
border-radius: $message-radius |
||||
font-size: $size-normal |
||||
strong |
||||
color: currentColor |
||||
a:not(.button):not(.tag):not(.dropdown-item) |
||||
color: currentColor |
||||
text-decoration: underline |
||||
// Sizes |
||||
&.is-small |
||||
font-size: $size-small |
||||
&.is-medium |
||||
font-size: $size-medium |
||||
&.is-large |
||||
font-size: $size-large |
||||
// Colors |
||||
@each $name, $components in $message-colors |
||||
$color: nth($components, 1) |
||||
$color-invert: nth($components, 2) |
||||
$color-light: null |
||||
$color-dark: null |
||||
|
||||
@if length($components) >= 3 |
||||
$color-light: nth($components, 3) |
||||
@if length($components) >= 4 |
||||
$color-dark: nth($components, 4) |
||||
@else |
||||
$color-luminance: colorLuminance($color) |
||||
$darken-percentage: $color-luminance * 70% |
||||
$desaturate-percentage: $color-luminance * 30% |
||||
$color-dark: desaturate(darken($color, $darken-percentage), $desaturate-percentage) |
||||
@else |
||||
$color-lightning: max((100% - lightness($color)) - 2%, 0%) |
||||
$color-light: lighten($color, $color-lightning) |
||||
|
||||
&.is-#{$name} |
||||
background-color: $color-light |
||||
.message-header |
||||
background-color: $color |
||||
color: $color-invert |
||||
.message-body |
||||
border-color: $color |
||||
color: $color-dark |
||||
|
||||
.message-header |
||||
align-items: center |
||||
background-color: $message-header-background-color |
||||
border-radius: $message-header-radius $message-header-radius 0 0 |
||||
color: $message-header-color |
||||
display: flex |
||||
font-weight: $message-header-weight |
||||
justify-content: space-between |
||||
line-height: 1.25 |
||||
padding: $message-header-padding |
||||
position: relative |
||||
.delete |
||||
flex-grow: 0 |
||||
flex-shrink: 0 |
||||
+ltr-property("margin", 0.75em, false) |
||||
& + .message-body |
||||
border-width: $message-header-body-border-width |
||||
border-top-left-radius: 0 |
||||
border-top-right-radius: 0 |
||||
|
||||
.message-body |
||||
border-color: $message-body-border-color |
||||
border-radius: $message-body-radius |
||||
border-style: solid |
||||
border-width: $message-body-border-width |
||||
color: $message-body-color |
||||
padding: $message-body-padding |
||||
code, |
||||
pre |
||||
background-color: $message-body-pre-background-color |
||||
pre code |
||||
background-color: $message-body-pre-code-background-color |
@ -0,0 +1,115 @@ |
||||
$modal-z: 40 !default |
||||
|
||||
$modal-background-background-color: bulmaRgba($scheme-invert, 0.86) !default |
||||
|
||||
$modal-content-width: 640px !default |
||||
$modal-content-margin-mobile: 20px !default |
||||
$modal-content-spacing-mobile: 160px !default |
||||
$modal-content-spacing-tablet: 40px !default |
||||
|
||||
$modal-close-dimensions: 40px !default |
||||
$modal-close-right: 20px !default |
||||
$modal-close-top: 20px !default |
||||
|
||||
$modal-card-spacing: 40px !default |
||||
|
||||
$modal-card-head-background-color: $background !default |
||||
$modal-card-head-border-bottom: 1px solid $border !default |
||||
$modal-card-head-padding: 20px !default |
||||
$modal-card-head-radius: $radius-large !default |
||||
|
||||
$modal-card-title-color: $text-strong !default |
||||
$modal-card-title-line-height: 1 !default |
||||
$modal-card-title-size: $size-4 !default |
||||
|
||||
$modal-card-foot-radius: $radius-large !default |
||||
$modal-card-foot-border-top: 1px solid $border !default |
||||
|
||||
$modal-card-body-background-color: $scheme-main !default |
||||
$modal-card-body-padding: 20px !default |
||||
|
||||
$modal-breakpoint: $tablet !default |
||||
|
||||
.modal |
||||
@extend %overlay |
||||
align-items: center |
||||
display: none |
||||
flex-direction: column |
||||
justify-content: center |
||||
overflow: hidden |
||||
position: fixed |
||||
z-index: $modal-z |
||||
// Modifiers |
||||
&.is-active |
||||
display: flex |
||||
|
||||
.modal-background |
||||
@extend %overlay |
||||
background-color: $modal-background-background-color |
||||
|
||||
.modal-content, |
||||
.modal-card |
||||
margin: 0 $modal-content-margin-mobile |
||||
max-height: calc(100vh - #{$modal-content-spacing-mobile}) |
||||
overflow: auto |
||||
position: relative |
||||
width: 100% |
||||
// Responsiveness |
||||
+from($modal-breakpoint) |
||||
margin: 0 auto |
||||
max-height: calc(100vh - #{$modal-content-spacing-tablet}) |
||||
width: $modal-content-width |
||||
|
||||
.modal-close |
||||
@extend %delete |
||||
background: none |
||||
height: $modal-close-dimensions |
||||
position: fixed |
||||
+ltr-position($modal-close-right) |
||||
top: $modal-close-top |
||||
width: $modal-close-dimensions |
||||
|
||||
.modal-card |
||||
display: flex |
||||
flex-direction: column |
||||
max-height: calc(100vh - #{$modal-card-spacing}) |
||||
overflow: hidden |
||||
-ms-overflow-y: visible |
||||
|
||||
.modal-card-head, |
||||
.modal-card-foot |
||||
align-items: center |
||||
background-color: $modal-card-head-background-color |
||||
display: flex |
||||
flex-shrink: 0 |
||||
justify-content: flex-start |
||||
padding: $modal-card-head-padding |
||||
position: relative |
||||
|
||||
.modal-card-head |
||||
border-bottom: $modal-card-head-border-bottom |
||||
border-top-left-radius: $modal-card-head-radius |
||||
border-top-right-radius: $modal-card-head-radius |
||||
|
||||
.modal-card-title |
||||
color: $modal-card-title-color |
||||
flex-grow: 1 |
||||
flex-shrink: 0 |
||||
font-size: $modal-card-title-size |
||||
line-height: $modal-card-title-line-height |
||||
|
||||
.modal-card-foot |
||||
border-bottom-left-radius: $modal-card-foot-radius |
||||
border-bottom-right-radius: $modal-card-foot-radius |
||||
border-top: $modal-card-foot-border-top |
||||
.button |
||||
&:not(:last-child) |
||||
+ltr-property("margin", 0.5em) |
||||
|
||||
.modal-card-body |
||||
+overflow-touch |
||||
background-color: $modal-card-body-background-color |
||||
flex-grow: 1 |
||||
flex-shrink: 1 |
||||
overflow: auto |
||||
padding: $modal-card-body-padding |
@ -0,0 +1,443 @@ |
||||
$navbar-background-color: $scheme-main !default |
||||
$navbar-box-shadow-size: 0 2px 0 0 !default |
||||
$navbar-box-shadow-color: $background !default |
||||
$navbar-height: 3.25rem !default |
||||
$navbar-padding-vertical: 1rem !default |
||||
$navbar-padding-horizontal: 2rem !default |
||||
$navbar-z: 30 !default |
||||
$navbar-fixed-z: 30 !default |
||||
|
||||
$navbar-item-color: $text !default |
||||
$navbar-item-hover-color: $link !default |
||||
$navbar-item-hover-background-color: $scheme-main-bis !default |
||||
$navbar-item-active-color: $scheme-invert !default |
||||
$navbar-item-active-background-color: transparent !default |
||||
$navbar-item-img-max-height: 1.75rem !default |
||||
|
||||
$navbar-burger-color: $navbar-item-color !default |
||||
|
||||
$navbar-tab-hover-background-color: transparent !default |
||||
$navbar-tab-hover-border-bottom-color: $link !default |
||||
$navbar-tab-active-color: $link !default |
||||
$navbar-tab-active-background-color: transparent !default |
||||
$navbar-tab-active-border-bottom-color: $link !default |
||||
$navbar-tab-active-border-bottom-style: solid !default |
||||
$navbar-tab-active-border-bottom-width: 3px !default |
||||
|
||||
$navbar-dropdown-background-color: $scheme-main !default |
||||
$navbar-dropdown-border-top: 2px solid $border !default |
||||
$navbar-dropdown-offset: -4px !default |
||||
$navbar-dropdown-arrow: $link !default |
||||
$navbar-dropdown-radius: $radius-large !default |
||||
$navbar-dropdown-z: 20 !default |
||||
|
||||
$navbar-dropdown-boxed-radius: $radius-large !default |
||||
$navbar-dropdown-boxed-shadow: 0 8px 8px bulmaRgba($scheme-invert, 0.1), 0 0 0 1px bulmaRgba($scheme-invert, 0.1) !default |
||||
|
||||
$navbar-dropdown-item-hover-color: $scheme-invert !default |
||||
$navbar-dropdown-item-hover-background-color: $background !default |
||||
$navbar-dropdown-item-active-color: $link !default |
||||
$navbar-dropdown-item-active-background-color: $background !default |
||||
|
||||
$navbar-divider-background-color: $background !default |
||||
$navbar-divider-height: 2px !default |
||||
|
||||
$navbar-bottom-box-shadow-size: 0 -2px 0 0 !default |
||||
|
||||
$navbar-breakpoint: $desktop !default |
||||
|
||||
$navbar-colors: $colors !default |
||||
|
||||
=navbar-fixed |
||||
left: 0 |
||||
position: fixed |
||||
right: 0 |
||||
z-index: $navbar-fixed-z |
||||
|
||||
.navbar |
||||
background-color: $navbar-background-color |
||||
min-height: $navbar-height |
||||
position: relative |
||||
z-index: $navbar-z |
||||
@each $name, $pair in $navbar-colors |
||||
$color: nth($pair, 1) |
||||
$color-invert: nth($pair, 2) |
||||
&.is-#{$name} |
||||
background-color: $color |
||||
color: $color-invert |
||||
.navbar-brand |
||||
& > .navbar-item, |
||||
.navbar-link |
||||
color: $color-invert |
||||
& > a.navbar-item, |
||||
.navbar-link |
||||
&:focus, |
||||
&:hover, |
||||
&.is-active |
||||
background-color: bulmaDarken($color, 5%) |
||||
color: $color-invert |
||||
.navbar-link |
||||
&::after |
||||
border-color: $color-invert |
||||
.navbar-burger |
||||
color: $color-invert |
||||
+from($navbar-breakpoint) |
||||
.navbar-start, |
||||
.navbar-end |
||||
& > .navbar-item, |
||||
.navbar-link |
||||
color: $color-invert |
||||
& > a.navbar-item, |
||||
.navbar-link |
||||
&:focus, |
||||
&:hover, |
||||
&.is-active |
||||
background-color: bulmaDarken($color, 5%) |
||||
color: $color-invert |
||||
.navbar-link |
||||
&::after |
||||
border-color: $color-invert |
||||
.navbar-item.has-dropdown:focus .navbar-link, |
||||
.navbar-item.has-dropdown:hover .navbar-link, |
||||
.navbar-item.has-dropdown.is-active .navbar-link |
||||
background-color: bulmaDarken($color, 5%) |
||||
color: $color-invert |
||||
.navbar-dropdown |
||||
a.navbar-item |
||||
&.is-active |
||||
background-color: $color |
||||
color: $color-invert |
||||
& > .container |
||||
align-items: stretch |
||||
display: flex |
||||
min-height: $navbar-height |
||||
width: 100% |
||||
&.has-shadow |
||||
box-shadow: $navbar-box-shadow-size $navbar-box-shadow-color |
||||
&.is-fixed-bottom, |
||||
&.is-fixed-top |
||||
+navbar-fixed |
||||
&.is-fixed-bottom |
||||
bottom: 0 |
||||
&.has-shadow |
||||
box-shadow: $navbar-bottom-box-shadow-size $navbar-box-shadow-color |
||||
&.is-fixed-top |
||||
top: 0 |
||||
|
||||
html, |
||||
body |
||||
&.has-navbar-fixed-top |
||||
padding-top: $navbar-height |
||||
&.has-navbar-fixed-bottom |
||||
padding-bottom: $navbar-height |
||||
|
||||
.navbar-brand, |
||||
.navbar-tabs |
||||
align-items: stretch |
||||
display: flex |
||||
flex-shrink: 0 |
||||
min-height: $navbar-height |
||||
|
||||
.navbar-brand |
||||
a.navbar-item |
||||
&:focus, |
||||
&:hover |
||||
background-color: transparent |
||||
|
||||
.navbar-tabs |
||||
+overflow-touch |
||||
max-width: 100vw |
||||
overflow-x: auto |
||||
overflow-y: hidden |
||||
|
||||
.navbar-burger |
||||
color: $navbar-burger-color |
||||
+hamburger($navbar-height) |
||||
+ltr-property("margin", auto, false) |
||||
|
||||
.navbar-menu |
||||
display: none |
||||
|
||||
.navbar-item, |
||||
.navbar-link |
||||
color: $navbar-item-color |
||||
display: block |
||||
line-height: 1.5 |
||||
padding: 0.5rem 0.75rem |
||||
position: relative |
||||
.icon |
||||
&:only-child |
||||
margin-left: -0.25rem |
||||
margin-right: -0.25rem |
||||
|
||||
a.navbar-item, |
||||
.navbar-link |
||||
cursor: pointer |
||||
&:focus, |
||||
&:focus-within, |
||||
&:hover, |
||||
&.is-active |
||||
background-color: $navbar-item-hover-background-color |
||||
color: $navbar-item-hover-color |
||||
|
||||
.navbar-item |
||||
flex-grow: 0 |
||||
flex-shrink: 0 |
||||
img |
||||
max-height: $navbar-item-img-max-height |
||||
&.has-dropdown |
||||
padding: 0 |
||||
&.is-expanded |
||||
flex-grow: 1 |
||||
flex-shrink: 1 |
||||
&.is-tab |
||||
border-bottom: 1px solid transparent |
||||
min-height: $navbar-height |
||||
padding-bottom: calc(0.5rem - 1px) |
||||
&:focus, |
||||
&:hover |
||||
background-color: $navbar-tab-hover-background-color |
||||
border-bottom-color: $navbar-tab-hover-border-bottom-color |
||||
&.is-active |
||||
background-color: $navbar-tab-active-background-color |
||||
border-bottom-color: $navbar-tab-active-border-bottom-color |
||||
border-bottom-style: $navbar-tab-active-border-bottom-style |
||||
border-bottom-width: $navbar-tab-active-border-bottom-width |
||||
color: $navbar-tab-active-color |
||||
padding-bottom: calc(0.5rem - #{$navbar-tab-active-border-bottom-width}) |
||||
|
||||
.navbar-content |
||||
flex-grow: 1 |
||||
flex-shrink: 1 |
||||
|
||||
.navbar-link:not(.is-arrowless) |
||||
+ltr-property("padding", 2.5em) |
||||
&::after |
||||
@extend %arrow |
||||
border-color: $navbar-dropdown-arrow |
||||
margin-top: -0.375em |
||||
+ltr-position(1.125em) |
||||
|
||||
.navbar-dropdown |
||||
font-size: 0.875rem |
||||
padding-bottom: 0.5rem |
||||
padding-top: 0.5rem |
||||
.navbar-item |
||||
padding-left: 1.5rem |
||||
padding-right: 1.5rem |
||||
|
||||
.navbar-divider |
||||
background-color: $navbar-divider-background-color |
||||
border: none |
||||
display: none |
||||
height: $navbar-divider-height |
||||
margin: 0.5rem 0 |
||||
|
||||
+until($navbar-breakpoint) |
||||
.navbar > .container |
||||
display: block |
||||
.navbar-brand, |
||||
.navbar-tabs |
||||
.navbar-item |
||||
align-items: center |
||||
display: flex |
||||
.navbar-link |
||||
&::after |
||||
display: none |
||||
.navbar-menu |
||||
background-color: $navbar-background-color |
||||
box-shadow: 0 8px 16px bulmaRgba($scheme-invert, 0.1) |
||||
padding: 0.5rem 0 |
||||
&.is-active |
||||
display: block |
||||
// Fixed navbar |
||||
.navbar |
||||
&.is-fixed-bottom-touch, |
||||
&.is-fixed-top-touch |
||||
+navbar-fixed |
||||
&.is-fixed-bottom-touch |
||||
bottom: 0 |
||||
&.has-shadow |
||||
box-shadow: 0 -2px 3px bulmaRgba($scheme-invert, 0.1) |
||||
&.is-fixed-top-touch |
||||
top: 0 |
||||
&.is-fixed-top, |
||||
&.is-fixed-top-touch |
||||
.navbar-menu |
||||
+overflow-touch |
||||
max-height: calc(100vh - #{$navbar-height}) |
||||
overflow: auto |
||||
html, |
||||
body |
||||
&.has-navbar-fixed-top-touch |
||||
padding-top: $navbar-height |
||||
&.has-navbar-fixed-bottom-touch |
||||
padding-bottom: $navbar-height |
||||
|
||||
+from($navbar-breakpoint) |
||||
.navbar, |
||||
.navbar-menu, |
||||
.navbar-start, |
||||
.navbar-end |
||||
align-items: stretch |
||||
display: flex |
||||
.navbar |
||||
min-height: $navbar-height |
||||
&.is-spaced |
||||
padding: $navbar-padding-vertical $navbar-padding-horizontal |
||||
.navbar-start, |
||||
.navbar-end |
||||
align-items: center |
||||
a.navbar-item, |
||||
.navbar-link |
||||
border-radius: $radius |
||||
&.is-transparent |
||||
a.navbar-item, |
||||
.navbar-link |
||||
&:focus, |
||||
&:hover, |
||||
&.is-active |
||||
background-color: transparent !important |
||||
.navbar-item.has-dropdown |
||||
&.is-active, |
||||
&.is-hoverable:focus, |
||||
&.is-hoverable:focus-within, |
||||
&.is-hoverable:hover |
||||
.navbar-link |
||||
background-color: transparent !important |
||||
.navbar-dropdown |
||||
a.navbar-item |
||||
&:focus, |
||||
&:hover |
||||
background-color: $navbar-dropdown-item-hover-background-color |
||||
color: $navbar-dropdown-item-hover-color |
||||
&.is-active |
||||
background-color: $navbar-dropdown-item-active-background-color |
||||
color: $navbar-dropdown-item-active-color |
||||
.navbar-burger |
||||
display: none |
||||
.navbar-item, |
||||
.navbar-link |
||||
align-items: center |
||||
display: flex |
||||
.navbar-item |
||||
&.has-dropdown |
||||
align-items: stretch |
||||
&.has-dropdown-up |
||||
.navbar-link::after |
||||
transform: rotate(135deg) translate(0.25em, -0.25em) |
||||
.navbar-dropdown |
||||
border-bottom: $navbar-dropdown-border-top |
||||
border-radius: $navbar-dropdown-radius $navbar-dropdown-radius 0 0 |
||||
border-top: none |
||||
bottom: 100% |
||||
box-shadow: 0 -8px 8px bulmaRgba($scheme-invert, 0.1) |
||||
top: auto |
||||
&.is-active, |
||||
&.is-hoverable:focus, |
||||
&.is-hoverable:focus-within, |
||||
&.is-hoverable:hover |
||||
.navbar-dropdown |
||||
display: block |
||||
.navbar.is-spaced &, |
||||
&.is-boxed |
||||
opacity: 1 |
||||
pointer-events: auto |
||||
transform: translateY(0) |
||||
.navbar-menu |
||||
flex-grow: 1 |
||||
flex-shrink: 0 |
||||
.navbar-start |
||||
justify-content: flex-start |
||||
+ltr-property("margin", auto) |
||||
.navbar-end |
||||
justify-content: flex-end |
||||
+ltr-property("margin", auto, false) |
||||
.navbar-dropdown |
||||
background-color: $navbar-dropdown-background-color |
||||
border-bottom-left-radius: $navbar-dropdown-radius |
||||
border-bottom-right-radius: $navbar-dropdown-radius |
||||
border-top: $navbar-dropdown-border-top |
||||
box-shadow: 0 8px 8px bulmaRgba($scheme-invert, 0.1) |
||||
display: none |
||||
font-size: 0.875rem |
||||
+ltr-position(0, false) |
||||
min-width: 100% |
||||
position: absolute |
||||
top: 100% |
||||
z-index: $navbar-dropdown-z |
||||
.navbar-item |
||||
padding: 0.375rem 1rem |
||||
white-space: nowrap |
||||
a.navbar-item |
||||
+ltr-property("padding", 3rem) |
||||
&:focus, |
||||
&:hover |
||||
background-color: $navbar-dropdown-item-hover-background-color |
||||
color: $navbar-dropdown-item-hover-color |
||||
&.is-active |
||||
background-color: $navbar-dropdown-item-active-background-color |
||||
color: $navbar-dropdown-item-active-color |
||||
.navbar.is-spaced &, |
||||
&.is-boxed |
||||
border-radius: $navbar-dropdown-boxed-radius |
||||
border-top: none |
||||
box-shadow: $navbar-dropdown-boxed-shadow |
||||
display: block |
||||
opacity: 0 |
||||
pointer-events: none |
||||
top: calc(100% + (#{$navbar-dropdown-offset})) |
||||
transform: translateY(-5px) |
||||
transition-duration: $speed |
||||
transition-property: opacity, transform |
||||
&.is-right |
||||
left: auto |
||||
right: 0 |
||||
.navbar-divider |
||||
display: block |
||||
.navbar > .container, |
||||
.container > .navbar |
||||
.navbar-brand |
||||
+ltr-property("margin", -.75rem, false) |
||||
.navbar-menu |
||||
+ltr-property("margin", -.75rem) |
||||
// Fixed navbar |
||||
.navbar |
||||
&.is-fixed-bottom-desktop, |
||||
&.is-fixed-top-desktop |
||||
+navbar-fixed |
||||
&.is-fixed-bottom-desktop |
||||
bottom: 0 |
||||
&.has-shadow |
||||
box-shadow: 0 -2px 3px bulmaRgba($scheme-invert, 0.1) |
||||
&.is-fixed-top-desktop |
||||
top: 0 |
||||
html, |
||||
body |
||||
&.has-navbar-fixed-top-desktop |
||||
padding-top: $navbar-height |
||||
&.has-navbar-fixed-bottom-desktop |
||||
padding-bottom: $navbar-height |
||||
&.has-spaced-navbar-fixed-top |
||||
padding-top: $navbar-height + ($navbar-padding-vertical * 2) |
||||
&.has-spaced-navbar-fixed-bottom |
||||
padding-bottom: $navbar-height + ($navbar-padding-vertical * 2) |
||||
// Hover/Active states |
||||
a.navbar-item, |
||||
.navbar-link |
||||
&.is-active |
||||
color: $navbar-item-active-color |
||||
&.is-active:not(:focus):not(:hover) |
||||
background-color: $navbar-item-active-background-color |
||||
.navbar-item.has-dropdown |
||||
&:focus, |
||||
&:hover, |
||||
&.is-active |
||||
.navbar-link |
||||
background-color: $navbar-item-hover-background-color |
||||
|
||||
// Combination |
||||
|
||||
.hero |
||||
&.is-fullheight-with-navbar |
||||
min-height: calc(100vh - #{$navbar-height}) |
@ -0,0 +1,150 @@ |
||||
$pagination-color: $text-strong !default |
||||
$pagination-border-color: $border !default |
||||
$pagination-margin: -0.25rem !default |
||||
$pagination-min-width: $control-height !default |
||||
|
||||
$pagination-item-font-size: 1em !default |
||||
$pagination-item-margin: 0.25rem !default |
||||
$pagination-item-padding-left: 0.5em !default |
||||
$pagination-item-padding-right: 0.5em !default |
||||
|
||||
$pagination-hover-color: $link-hover !default |
||||
$pagination-hover-border-color: $link-hover-border !default |
||||
|
||||
$pagination-focus-color: $link-focus !default |
||||
$pagination-focus-border-color: $link-focus-border !default |
||||
|
||||
$pagination-active-color: $link-active !default |
||||
$pagination-active-border-color: $link-active-border !default |
||||
|
||||
$pagination-disabled-color: $text-light !default |
||||
$pagination-disabled-background-color: $border !default |
||||
$pagination-disabled-border-color: $border !default |
||||
|
||||
$pagination-current-color: $link-invert !default |
||||
$pagination-current-background-color: $link !default |
||||
$pagination-current-border-color: $link !default |
||||
|
||||
$pagination-ellipsis-color: $grey-light !default |
||||
|
||||
$pagination-shadow-inset: inset 0 1px 2px rgba($scheme-invert, 0.2) |
||||
|
||||
.pagination |
||||
@extend %block |
||||
font-size: $size-normal |
||||
margin: $pagination-margin |
||||
// Sizes |
||||
&.is-small |
||||
font-size: $size-small |
||||
&.is-medium |
||||
font-size: $size-medium |
||||
&.is-large |
||||
font-size: $size-large |
||||
&.is-rounded |
||||
.pagination-previous, |
||||
.pagination-next |
||||
padding-left: 1em |
||||
padding-right: 1em |
||||
border-radius: $radius-rounded |
||||
.pagination-link |
||||
border-radius: $radius-rounded |
||||
|
||||
.pagination, |
||||
.pagination-list |
||||
align-items: center |
||||
display: flex |
||||
justify-content: center |
||||
text-align: center |
||||
|
||||
.pagination-previous, |
||||
.pagination-next, |
||||
.pagination-link, |
||||
.pagination-ellipsis |
||||
@extend %control |
||||
@extend %unselectable |
||||
font-size: $pagination-item-font-size |
||||
justify-content: center |
||||
margin: $pagination-item-margin |
||||
padding-left: $pagination-item-padding-left |
||||
padding-right: $pagination-item-padding-right |
||||
text-align: center |
||||
|
||||
.pagination-previous, |
||||
.pagination-next, |
||||
.pagination-link |
||||
border-color: $pagination-border-color |
||||
color: $pagination-color |
||||
min-width: $pagination-min-width |
||||
&:hover |
||||
border-color: $pagination-hover-border-color |
||||
color: $pagination-hover-color |
||||
&:focus |
||||
border-color: $pagination-focus-border-color |
||||
&:active |
||||
box-shadow: $pagination-shadow-inset |
||||
&[disabled] |
||||
background-color: $pagination-disabled-background-color |
||||
border-color: $pagination-disabled-border-color |
||||
box-shadow: none |
||||
color: $pagination-disabled-color |
||||
opacity: 0.5 |
||||
|
||||
.pagination-previous, |
||||
.pagination-next |
||||
padding-left: 0.75em |
||||
padding-right: 0.75em |
||||
white-space: nowrap |
||||
|
||||
.pagination-link |
||||
&.is-current |
||||
background-color: $pagination-current-background-color |
||||
border-color: $pagination-current-border-color |
||||
color: $pagination-current-color |
||||
|
||||
.pagination-ellipsis |
||||
color: $pagination-ellipsis-color |
||||
pointer-events: none |
||||
|
||||
.pagination-list |
||||
flex-wrap: wrap |
||||
|
||||
+mobile |
||||
.pagination |
||||
flex-wrap: wrap |
||||
.pagination-previous, |
||||
.pagination-next |
||||
flex-grow: 1 |
||||
flex-shrink: 1 |
||||
.pagination-list |
||||
li |
||||
flex-grow: 1 |
||||
flex-shrink: 1 |
||||
|
||||
+tablet |
||||
.pagination-list |
||||
flex-grow: 1 |
||||
flex-shrink: 1 |
||||
justify-content: flex-start |
||||
order: 1 |
||||
.pagination-previous |
||||
order: 2 |
||||
.pagination-next |
||||
order: 3 |
||||
.pagination |
||||
justify-content: space-between |
||||
&.is-centered |
||||
.pagination-previous |
||||
order: 1 |
||||
.pagination-list |
||||
justify-content: center |
||||
order: 2 |
||||
.pagination-next |
||||
order: 3 |
||||
&.is-right |
||||
.pagination-previous |
||||
order: 1 |
||||
.pagination-next |
||||
order: 2 |
||||
.pagination-list |
||||
justify-content: flex-end |
||||
order: 3 |
@ -0,0 +1,119 @@ |
||||
$panel-margin: $block-spacing !default |
||||
$panel-item-border: 1px solid $border-light !default |
||||
$panel-radius: $radius-large !default |
||||
$panel-shadow: 0 0.5em 1em -0.125em rgba($scheme-invert, 0.1), 0 0px 0 1px rgba($scheme-invert, 0.02) !default |
||||
|
||||
$panel-heading-background-color: $border-light !default |
||||
$panel-heading-color: $text-strong !default |
||||
$panel-heading-line-height: 1.25 !default |
||||
$panel-heading-padding: 0.75em 1em !default |
||||
$panel-heading-radius: $radius !default |
||||
$panel-heading-size: 1.25em !default |
||||
$panel-heading-weight: $weight-bold !default |
||||
|
||||
$panel-tabs-font-size: 0.875em !default |
||||
$panel-tab-border-bottom: 1px solid $border !default |
||||
$panel-tab-active-border-bottom-color: $link-active-border !default |
||||
$panel-tab-active-color: $link-active !default |
||||
|
||||
$panel-list-item-color: $text !default |
||||
$panel-list-item-hover-color: $link !default |
||||
|
||||
$panel-block-color: $text-strong !default |
||||
$panel-block-hover-background-color: $background !default |
||||
$panel-block-active-border-left-color: $link !default |
||||
$panel-block-active-color: $link-active !default |
||||
$panel-block-active-icon-color: $link !default |
||||
|
||||
$panel-icon-color: $text-light !default |
||||
$panel-colors: $colors !default |
||||
|
||||
.panel |
||||
border-radius: $panel-radius |
||||
box-shadow: $panel-shadow |
||||
font-size: $size-normal |
||||
&:not(:last-child) |
||||
margin-bottom: $panel-margin |
||||
// Colors |
||||
@each $name, $components in $panel-colors |
||||
$color: nth($components, 1) |
||||
$color-invert: nth($components, 2) |
||||
&.is-#{$name} |
||||
.panel-heading |
||||
background-color: $color |
||||
color: $color-invert |
||||
.panel-tabs a.is-active |
||||
border-bottom-color: $color |
||||
.panel-block.is-active .panel-icon |
||||
color: $color |
||||
|
||||
.panel-tabs, |
||||
.panel-block |
||||
&:not(:last-child) |
||||
border-bottom: $panel-item-border |
||||
|
||||
.panel-heading |
||||
background-color: $panel-heading-background-color |
||||
border-radius: $panel-radius $panel-radius 0 0 |
||||
color: $panel-heading-color |
||||
font-size: $panel-heading-size |
||||
font-weight: $panel-heading-weight |
||||
line-height: $panel-heading-line-height |
||||
padding: $panel-heading-padding |
||||
|
||||
.panel-tabs |
||||
align-items: flex-end |
||||
display: flex |
||||
font-size: $panel-tabs-font-size |
||||
justify-content: center |
||||
a |
||||
border-bottom: $panel-tab-border-bottom |
||||
margin-bottom: -1px |
||||
padding: 0.5em |
||||
// Modifiers |
||||
&.is-active |
||||
border-bottom-color: $panel-tab-active-border-bottom-color |
||||
color: $panel-tab-active-color |
||||
|
||||
.panel-list |
||||
a |
||||
color: $panel-list-item-color |
||||
&:hover |
||||
color: $panel-list-item-hover-color |
||||
|
||||
.panel-block |
||||
align-items: center |
||||
color: $panel-block-color |
||||
display: flex |
||||
justify-content: flex-start |
||||
padding: 0.5em 0.75em |
||||
input[type="checkbox"] |
||||
+ltr-property("margin", 0.75em) |
||||
& > .control |
||||
flex-grow: 1 |
||||
flex-shrink: 1 |
||||
width: 100% |
||||
&.is-wrapped |
||||
flex-wrap: wrap |
||||
&.is-active |
||||
border-left-color: $panel-block-active-border-left-color |
||||
color: $panel-block-active-color |
||||
.panel-icon |
||||
color: $panel-block-active-icon-color |
||||
&:last-child |
||||
border-bottom-left-radius: $panel-radius |
||||
border-bottom-right-radius: $panel-radius |
||||
|
||||
a.panel-block, |
||||
label.panel-block |
||||
cursor: pointer |
||||
&:hover |
||||
background-color: $panel-block-hover-background-color |
||||
|
||||
.panel-icon |
||||
+fa(14px, 1em) |
||||
color: $panel-icon-color |
||||
+ltr-property("margin", 0.75em) |
||||
.fa |
||||
font-size: inherit |
||||
line-height: inherit |
@ -0,0 +1,174 @@ |
||||
$tabs-border-bottom-color: $border !default |
||||
$tabs-border-bottom-style: solid !default |
||||
$tabs-border-bottom-width: 1px !default |
||||
$tabs-link-color: $text !default |
||||
$tabs-link-hover-border-bottom-color: $text-strong !default |
||||
$tabs-link-hover-color: $text-strong !default |
||||
$tabs-link-active-border-bottom-color: $link !default |
||||
$tabs-link-active-color: $link !default |
||||
$tabs-link-padding: 0.5em 1em !default |
||||
|
||||
$tabs-boxed-link-radius: $radius !default |
||||
$tabs-boxed-link-hover-background-color: $background !default |
||||
$tabs-boxed-link-hover-border-bottom-color: $border !default |
||||
|
||||
$tabs-boxed-link-active-background-color: $scheme-main !default |
||||
$tabs-boxed-link-active-border-color: $border !default |
||||
$tabs-boxed-link-active-border-bottom-color: transparent !default |
||||
|
||||
$tabs-toggle-link-border-color: $border !default |
||||
$tabs-toggle-link-border-style: solid !default |
||||
$tabs-toggle-link-border-width: 1px !default |
||||
$tabs-toggle-link-hover-background-color: $background !default |
||||
$tabs-toggle-link-hover-border-color: $border-hover !default |
||||
$tabs-toggle-link-radius: $radius !default |
||||
$tabs-toggle-link-active-background-color: $link !default |
||||
$tabs-toggle-link-active-border-color: $link !default |
||||
$tabs-toggle-link-active-color: $link-invert !default |
||||
|
||||
.tabs |
||||
@extend %block |
||||
+overflow-touch |
||||
@extend %unselectable |
||||
align-items: stretch |
||||
display: flex |
||||
font-size: $size-normal |
||||
justify-content: space-between |
||||
overflow: hidden |
||||
overflow-x: auto |
||||
white-space: nowrap |
||||
a |
||||
align-items: center |
||||
border-bottom-color: $tabs-border-bottom-color |
||||
border-bottom-style: $tabs-border-bottom-style |
||||
border-bottom-width: $tabs-border-bottom-width |
||||
color: $tabs-link-color |
||||
display: flex |
||||
justify-content: center |
||||
margin-bottom: -#{$tabs-border-bottom-width} |
||||
padding: $tabs-link-padding |
||||
vertical-align: top |
||||
&:hover |
||||
border-bottom-color: $tabs-link-hover-border-bottom-color |
||||
color: $tabs-link-hover-color |
||||
li |
||||
display: block |
||||
&.is-active |
||||
a |
||||
border-bottom-color: $tabs-link-active-border-bottom-color |
||||
color: $tabs-link-active-color |
||||
ul |
||||
align-items: center |
||||
border-bottom-color: $tabs-border-bottom-color |
||||
border-bottom-style: $tabs-border-bottom-style |
||||
border-bottom-width: $tabs-border-bottom-width |
||||
display: flex |
||||
flex-grow: 1 |
||||
flex-shrink: 0 |
||||
justify-content: flex-start |
||||
&.is-left |
||||
padding-right: 0.75em |
||||
&.is-center |
||||
flex: none |
||||
justify-content: center |
||||
padding-left: 0.75em |
||||
padding-right: 0.75em |
||||
&.is-right |
||||
justify-content: flex-end |
||||
padding-left: 0.75em |
||||
.icon |
||||
&:first-child |
||||
+ltr-property("margin", 0.5em) |
||||
&:last-child |
||||
+ltr-property("margin", 0.5em, false) |
||||
// Alignment |
||||
&.is-centered |
||||
ul |
||||
justify-content: center |
||||
&.is-right |
||||
ul |
||||
justify-content: flex-end |
||||
// Styles |
||||
&.is-boxed |
||||
a |
||||
border: 1px solid transparent |
||||
+ltr |
||||
border-radius: $tabs-boxed-link-radius $tabs-boxed-link-radius 0 0 |
||||
+rtl |
||||
border-radius: 0 0 $tabs-boxed-link-radius $tabs-boxed-link-radius |
||||
&:hover |
||||
background-color: $tabs-boxed-link-hover-background-color |
||||
border-bottom-color: $tabs-boxed-link-hover-border-bottom-color |
||||
li |
||||
&.is-active |
||||
a |
||||
background-color: $tabs-boxed-link-active-background-color |
||||
border-color: $tabs-boxed-link-active-border-color |
||||
border-bottom-color: $tabs-boxed-link-active-border-bottom-color !important |
||||
&.is-fullwidth |
||||
li |
||||
flex-grow: 1 |
||||
flex-shrink: 0 |
||||
&.is-toggle |
||||
a |
||||
border-color: $tabs-toggle-link-border-color |
||||
border-style: $tabs-toggle-link-border-style |
||||
border-width: $tabs-toggle-link-border-width |
||||
margin-bottom: 0 |
||||
position: relative |
||||
&:hover |
||||
background-color: $tabs-toggle-link-hover-background-color |
||||
border-color: $tabs-toggle-link-hover-border-color |
||||
z-index: 2 |
||||
li |
||||
& + li |
||||
+ltr-property("margin", -#{$tabs-toggle-link-border-width}, false) |
||||
&:first-child a |
||||
+ltr |
||||
border-top-left-radius: $tabs-toggle-link-radius |
||||
border-bottom-left-radius: $tabs-toggle-link-radius |
||||
+rtl |
||||
border-top-right-radius: $tabs-toggle-link-radius |
||||
border-bottom-right-radius: $tabs-toggle-link-radius |
||||
&:last-child a |
||||
+ltr |
||||
border-top-right-radius: $tabs-toggle-link-radius |
||||
border-bottom-right-radius: $tabs-toggle-link-radius |
||||
+rtl |
||||
border-top-left-radius: $tabs-toggle-link-radius |
||||
border-bottom-left-radius: $tabs-toggle-link-radius |
||||
&.is-active |
||||
a |
||||
background-color: $tabs-toggle-link-active-background-color |
||||
border-color: $tabs-toggle-link-active-border-color |
||||
color: $tabs-toggle-link-active-color |
||||
z-index: 1 |
||||
ul |
||||
border-bottom: none |
||||
&.is-toggle-rounded |
||||
li |
||||
&:first-child a |
||||
+ltr |
||||
border-bottom-left-radius: $radius-rounded |
||||
border-top-left-radius: $radius-rounded |
||||
padding-left: 1.25em |
||||
+rtl |
||||
border-bottom-right-radius: $radius-rounded |
||||
border-top-right-radius: $radius-rounded |
||||
padding-right: 1.25em |
||||
&:last-child a |
||||
+ltr |
||||
border-bottom-right-radius: $radius-rounded |
||||
border-top-right-radius: $radius-rounded |
||||
padding-right: 1.25em |
||||
+rtl |
||||
border-bottom-left-radius: $radius-rounded |
||||
border-top-left-radius: $radius-rounded |
||||
padding-left: 1.25em |
||||
// Sizes |
||||
&.is-small |
||||
font-size: $size-small |
||||
&.is-medium |
||||
font-size: $size-medium |
||||
&.is-large |
||||
font-size: $size-large |
@ -0,0 +1,16 @@ |
||||
/* Bulma Elements */ |
||||
@charset "utf-8" |
||||
|
||||
@import "box.sass" |
||||
@import "button.sass" |
||||
@import "container.sass" |
||||
@import "content.sass" |
||||
@import "icon.sass" |
||||
@import "image.sass" |
||||
@import "notification.sass" |
||||
@import "progress.sass" |
||||
@import "table.sass" |
||||
@import "tag.sass" |
||||
@import "title.sass" |
||||
|
||||
@import "other.sass" |
@ -0,0 +1,24 @@ |
||||
$box-color: $text !default |
||||
$box-background-color: $scheme-main !default |
||||
$box-radius: $radius-large !default |
||||
$box-shadow: 0 0.5em 1em -0.125em rgba($scheme-invert, 0.1), 0 0px 0 1px rgba($scheme-invert, 0.02) !default |
||||
$box-padding: 1.25rem !default |
||||
|
||||
$box-link-hover-shadow: 0 0.5em 1em -0.125em rgba($scheme-invert, 0.1), 0 0 0 1px $link !default |
||||
$box-link-active-shadow: inset 0 1px 2px rgba($scheme-invert, 0.2), 0 0 0 1px $link !default |
||||
|
||||
.box |
||||
@extend %block |
||||
background-color: $box-background-color |
||||
border-radius: $box-radius |
||||
box-shadow: $box-shadow |
||||
color: $box-color |
||||
display: block |
||||
padding: $box-padding |
||||
|
||||
a.box |
||||
&:hover, |
||||
&:focus |
||||
box-shadow: $box-link-hover-shadow |
||||
&:active |
||||
box-shadow: $box-link-active-shadow |
@ -0,0 +1,325 @@ |
||||
$button-color: $text-strong !default |
||||
$button-background-color: $scheme-main !default |
||||
$button-family: false !default |
||||
|
||||
$button-border-color: $border !default |
||||
$button-border-width: $control-border-width !default |
||||
|
||||
$button-padding-vertical: calc(0.5em - #{$button-border-width}) !default |
||||
$button-padding-horizontal: 1em !default |
||||
|
||||
$button-hover-color: $link-hover !default |
||||
$button-hover-border-color: $link-hover-border !default |
||||
|
||||
$button-focus-color: $link-focus !default |
||||
$button-focus-border-color: $link-focus-border !default |
||||
$button-focus-box-shadow-size: 0 0 0 0.125em !default |
||||
$button-focus-box-shadow-color: bulmaRgba($link, 0.25) !default |
||||
|
||||
$button-active-color: $link-active !default |
||||
$button-active-border-color: $link-active-border !default |
||||
|
||||
$button-text-color: $text !default |
||||
$button-text-decoration: underline !default |
||||
$button-text-hover-background-color: $background !default |
||||
$button-text-hover-color: $text-strong !default |
||||
|
||||
$button-disabled-background-color: $scheme-main !default |
||||
$button-disabled-border-color: $border !default |
||||
$button-disabled-shadow: none !default |
||||
$button-disabled-opacity: 0.5 !default |
||||
|
||||
$button-static-color: $text-light !default |
||||
$button-static-background-color: $scheme-main-ter !default |
||||
$button-static-border-color: $border !default |
||||
|
||||
$button-colors: $colors !default |
||||
|
||||
// The button sizes use mixins so they can be used at different breakpoints |
||||
=button-small |
||||
border-radius: $radius-small |
||||
font-size: $size-small |
||||
=button-normal |
||||
font-size: $size-normal |
||||
=button-medium |
||||
font-size: $size-medium |
||||
=button-large |
||||
font-size: $size-large |
||||
|
||||
.button |
||||
@extend %control |
||||
@extend %unselectable |
||||
background-color: $button-background-color |
||||
border-color: $button-border-color |
||||
border-width: $button-border-width |
||||
color: $button-color |
||||
cursor: pointer |
||||
@if $button-family |
||||
font-family: $button-family |
||||
justify-content: center |
||||
padding-bottom: $button-padding-vertical |
||||
padding-left: $button-padding-horizontal |
||||
padding-right: $button-padding-horizontal |
||||
padding-top: $button-padding-vertical |
||||
text-align: center |
||||
white-space: nowrap |
||||
strong |
||||
color: inherit |
||||
.icon |
||||
&, |
||||
&.is-small, |
||||
&.is-medium, |
||||
&.is-large |
||||
height: 1.5em |
||||
width: 1.5em |
||||
&:first-child:not(:last-child) |
||||
+ltr-property("margin", calc(#{-1 / 2 * $button-padding-horizontal} - #{$button-border-width}), false) |
||||
+ltr-property("margin", $button-padding-horizontal / 4) |
||||
&:last-child:not(:first-child) |
||||
+ltr-property("margin", $button-padding-horizontal / 4, false) |
||||
+ltr-property("margin", calc(#{-1 / 2 * $button-padding-horizontal} - #{$button-border-width})) |
||||
&:first-child:last-child |
||||
margin-left: calc(#{-1 / 2 * $button-padding-horizontal} - #{$button-border-width}) |
||||
margin-right: calc(#{-1 / 2 * $button-padding-horizontal} - #{$button-border-width}) |
||||
// States |
||||
&:hover, |
||||
&.is-hovered |
||||
border-color: $button-hover-border-color |
||||
color: $button-hover-color |
||||
&:focus, |
||||
&.is-focused |
||||
border-color: $button-focus-border-color |
||||
color: $button-focus-color |
||||
&:not(:active) |
||||
box-shadow: $button-focus-box-shadow-size $button-focus-box-shadow-color |
||||
&:active, |
||||
&.is-active |
||||
border-color: $button-active-border-color |
||||
color: $button-active-color |
||||
// Colors |
||||
&.is-text |
||||
background-color: transparent |
||||
border-color: transparent |
||||
color: $button-text-color |
||||
text-decoration: $button-text-decoration |
||||
&:hover, |
||||
&.is-hovered, |
||||
&:focus, |
||||
&.is-focused |
||||
background-color: $button-text-hover-background-color |
||||
color: $button-text-hover-color |
||||
&:active, |
||||
&.is-active |
||||
background-color: bulmaDarken($button-text-hover-background-color, 5%) |
||||
color: $button-text-hover-color |
||||
&[disabled], |
||||
fieldset[disabled] & |
||||
background-color: transparent |
||||
border-color: transparent |
||||
box-shadow: none |
||||
@each $name, $pair in $button-colors |
||||
$color: nth($pair, 1) |
||||
$color-invert: nth($pair, 2) |
||||
&.is-#{$name} |
||||
background-color: $color |
||||
border-color: transparent |
||||
color: $color-invert |
||||
&:hover, |
||||
&.is-hovered |
||||
background-color: bulmaDarken($color, 2.5%) |
||||
border-color: transparent |
||||
color: $color-invert |
||||
&:focus, |
||||
&.is-focused |
||||
border-color: transparent |
||||
color: $color-invert |
||||
&:not(:active) |
||||
box-shadow: $button-focus-box-shadow-size bulmaRgba($color, 0.25) |
||||
&:active, |
||||
&.is-active |
||||
background-color: bulmaDarken($color, 5%) |
||||
border-color: transparent |
||||
color: $color-invert |
||||
&[disabled], |
||||
fieldset[disabled] & |
||||
background-color: $color |
||||
border-color: transparent |
||||
box-shadow: none |
||||
&.is-inverted |
||||
background-color: $color-invert |
||||
color: $color |
||||
&:hover, |
||||
&.is-hovered |
||||
background-color: bulmaDarken($color-invert, 5%) |
||||
&[disabled], |
||||
fieldset[disabled] & |
||||
background-color: $color-invert |
||||
border-color: transparent |
||||
box-shadow: none |
||||
color: $color |
||||
&.is-loading |
||||
&::after |
||||
border-color: transparent transparent $color-invert $color-invert !important |
||||
&.is-outlined |
||||
background-color: transparent |
||||
border-color: $color |
||||
color: $color |
||||
&:hover, |
||||
&.is-hovered, |
||||
&:focus, |
||||
&.is-focused |
||||
background-color: $color |
||||
border-color: $color |
||||
color: $color-invert |
||||
&.is-loading |
||||
&::after |
||||
border-color: transparent transparent $color $color !important |
||||
&:hover, |
||||
&.is-hovered, |
||||
&:focus, |
||||
&.is-focused |
||||
&::after |
||||
border-color: transparent transparent $color-invert $color-invert !important |
||||
&[disabled], |
||||
fieldset[disabled] & |
||||
background-color: transparent |
||||
border-color: $color |
||||
box-shadow: none |
||||
color: $color |
||||
&.is-inverted.is-outlined |
||||
background-color: transparent |
||||
border-color: $color-invert |
||||
color: $color-invert |
||||
&:hover, |
||||
&.is-hovered, |
||||
&:focus, |
||||
&.is-focused |
||||
background-color: $color-invert |
||||
color: $color |
||||
&.is-loading |
||||
&:hover, |
||||
&.is-hovered, |
||||
&:focus, |
||||
&.is-focused |
||||
&::after |
||||
border-color: transparent transparent $color $color !important |
||||
&[disabled], |
||||
fieldset[disabled] & |
||||
background-color: transparent |
||||
border-color: $color-invert |
||||
box-shadow: none |
||||
color: $color-invert |
||||
// If light and dark colors are provided |
||||
@if length($pair) >= 4 |
||||
$color-light: nth($pair, 3) |
||||
$color-dark: nth($pair, 4) |
||||
&.is-light |
||||
background-color: $color-light |
||||
color: $color-dark |
||||
&:hover, |
||||
&.is-hovered |
||||
background-color: bulmaDarken($color-light, 2.5%) |
||||
border-color: transparent |
||||
color: $color-dark |
||||
&:active, |
||||
&.is-active |
||||
background-color: bulmaDarken($color-light, 5%) |
||||
border-color: transparent |
||||
color: $color-dark |
||||
// Sizes |
||||
&.is-small |
||||
+button-small |
||||
&.is-normal |
||||
+button-normal |
||||
&.is-medium |
||||
+button-medium |
||||
&.is-large |
||||
+button-large |
||||
// Modifiers |
||||
&[disabled], |
||||
fieldset[disabled] & |
||||
background-color: $button-disabled-background-color |
||||
border-color: $button-disabled-border-color |
||||
box-shadow: $button-disabled-shadow |
||||
opacity: $button-disabled-opacity |
||||
&.is-fullwidth |
||||
display: flex |
||||
width: 100% |
||||
&.is-loading |
||||
color: transparent !important |
||||
pointer-events: none |
||||
&::after |
||||
@extend %loader |
||||
+center(1em) |
||||
position: absolute !important |
||||
&.is-static |
||||
background-color: $button-static-background-color |
||||
border-color: $button-static-border-color |
||||
color: $button-static-color |
||||
box-shadow: none |
||||
pointer-events: none |
||||
&.is-rounded |
||||
border-radius: $radius-rounded |
||||
padding-left: calc(#{$button-padding-horizontal} + 0.25em) |
||||
padding-right: calc(#{$button-padding-horizontal} + 0.25em) |
||||
|
||||
.buttons |
||||
align-items: center |
||||
display: flex |
||||
flex-wrap: wrap |
||||
justify-content: flex-start |
||||
.button |
||||
margin-bottom: 0.5rem |
||||
&:not(:last-child):not(.is-fullwidth) |
||||
+ltr-property("margin", 0.5rem) |
||||
&:last-child |
||||
margin-bottom: -0.5rem |
||||
&:not(:last-child) |
||||
margin-bottom: 1rem |
||||
// Sizes |
||||
&.are-small |
||||
.button:not(.is-normal):not(.is-medium):not(.is-large) |
||||
+button-small |
||||
&.are-medium |
||||
.button:not(.is-small):not(.is-normal):not(.is-large) |
||||
+button-medium |
||||
&.are-large |
||||
.button:not(.is-small):not(.is-normal):not(.is-medium) |
||||
+button-large |
||||
&.has-addons |
||||
.button |
||||
&:not(:first-child) |
||||
border-bottom-left-radius: 0 |
||||
border-top-left-radius: 0 |
||||
&:not(:last-child) |
||||
border-bottom-right-radius: 0 |
||||
border-top-right-radius: 0 |
||||
+ltr-property("margin", -1px) |
||||
&:last-child |
||||
+ltr-property("margin", 0) |
||||
&:hover, |
||||
&.is-hovered |
||||
z-index: 2 |
||||
&:focus, |
||||
&.is-focused, |
||||
&:active, |
||||
&.is-active, |
||||
&.is-selected |
||||
z-index: 3 |
||||
&:hover |
||||
z-index: 4 |
||||
&.is-expanded |
||||
flex-grow: 1 |
||||
flex-shrink: 1 |
||||
&.is-centered |
||||
justify-content: center |
||||
&:not(.has-addons) |
||||
.button:not(.is-fullwidth) |
||||
margin-left: 0.25rem |
||||
margin-right: 0.25rem |
||||
&.is-right |
||||
justify-content: flex-end |
||||
&:not(.has-addons) |
||||
.button:not(.is-fullwidth) |
||||
margin-left: 0.25rem |
||||
margin-right: 0.25rem |
@ -0,0 +1,27 @@ |
||||
$container-offset: (2 * $gap) !default |
||||
$container-max-width: $fullhd !default |
||||
|
||||
.container |
||||
flex-grow: 1 |
||||
margin: 0 auto |
||||
position: relative |
||||
width: auto |
||||
&.is-fluid |
||||
max-width: none !important |
||||
padding-left: $gap |
||||
padding-right: $gap |
||||
width: 100% |
||||
+desktop |
||||
max-width: $desktop - $container-offset |
||||
+until-widescreen |
||||
&.is-widescreen:not(.is-max-desktop) |
||||
max-width: min($widescreen, $container-max-width) - $container-offset |
||||
+until-fullhd |
||||
&.is-fullhd:not(.is-max-desktop):not(.is-max-widescreen) |
||||
max-width: min($fullhd, $container-max-width) - $container-offset |
||||
+widescreen |
||||
&:not(.is-max-desktop) |
||||
max-width: min($widescreen, $container-max-width) - $container-offset |
||||
+fullhd |
||||
&:not(.is-max-desktop):not(.is-max-widescreen) |
||||
max-width: min($fullhd, $container-max-width) - $container-offset |
@ -0,0 +1,155 @@ |
||||
$content-heading-color: $text-strong !default |
||||
$content-heading-weight: $weight-semibold !default |
||||
$content-heading-line-height: 1.125 !default |
||||
|
||||
$content-blockquote-background-color: $background !default |
||||
$content-blockquote-border-left: 5px solid $border !default |
||||
$content-blockquote-padding: 1.25em 1.5em !default |
||||
|
||||
$content-pre-padding: 1.25em 1.5em !default |
||||
|
||||
$content-table-cell-border: 1px solid $border !default |
||||
$content-table-cell-border-width: 0 0 1px !default |
||||
$content-table-cell-padding: 0.5em 0.75em !default |
||||
$content-table-cell-heading-color: $text-strong !default |
||||
$content-table-head-cell-border-width: 0 0 2px !default |
||||
$content-table-head-cell-color: $text-strong !default |
||||
$content-table-foot-cell-border-width: 2px 0 0 !default |
||||
$content-table-foot-cell-color: $text-strong !default |
||||
|
||||
.content |
||||
@extend %block |
||||
// Inline |
||||
li + li |
||||
margin-top: 0.25em |
||||
// Block |
||||
p, |
||||
dl, |
||||
ol, |
||||
ul, |
||||
blockquote, |
||||
pre, |
||||
table |
||||
&:not(:last-child) |
||||
margin-bottom: 1em |
||||
h1, |
||||
h2, |
||||
h3, |
||||
h4, |
||||
h5, |
||||
h6 |
||||
color: $content-heading-color |
||||
font-weight: $content-heading-weight |
||||
line-height: $content-heading-line-height |
||||
h1 |
||||
font-size: 2em |
||||
margin-bottom: 0.5em |
||||
&:not(:first-child) |
||||
margin-top: 1em |
||||
h2 |
||||
font-size: 1.75em |
||||
margin-bottom: 0.5714em |
||||
&:not(:first-child) |
||||
margin-top: 1.1428em |
||||
h3 |
||||
font-size: 1.5em |
||||
margin-bottom: 0.6666em |
||||
&:not(:first-child) |
||||
margin-top: 1.3333em |
||||
h4 |
||||
font-size: 1.25em |
||||
margin-bottom: 0.8em |
||||
h5 |
||||
font-size: 1.125em |
||||
margin-bottom: 0.8888em |
||||
h6 |
||||
font-size: 1em |
||||
margin-bottom: 1em |
||||
blockquote |
||||
background-color: $content-blockquote-background-color |
||||
+ltr-property("border", $content-blockquote-border-left, false) |
||||
padding: $content-blockquote-padding |
||||
ol |
||||
list-style-position: outside |
||||
+ltr-property("margin", 2em, false) |
||||
margin-top: 1em |
||||
&:not([type]) |
||||
list-style-type: decimal |
||||
&.is-lower-alpha |
||||
list-style-type: lower-alpha |
||||
&.is-lower-roman |
||||
list-style-type: lower-roman |
||||
&.is-upper-alpha |
||||
list-style-type: upper-alpha |
||||
&.is-upper-roman |
||||
list-style-type: upper-roman |
||||
ul |
||||
list-style: disc outside |
||||
+ltr-property("margin", 2em, false) |
||||
margin-top: 1em |
||||
ul |
||||
list-style-type: circle |
||||
margin-top: 0.5em |
||||
ul |
||||
list-style-type: square |
||||
dd |
||||
+ltr-property("margin", 2em, false) |
||||
figure |
||||
margin-left: 2em |
||||
margin-right: 2em |
||||
text-align: center |
||||
&:not(:first-child) |
||||
margin-top: 2em |
||||
&:not(:last-child) |
||||
margin-bottom: 2em |
||||
img |
||||
display: inline-block |
||||
figcaption |
||||
font-style: italic |
||||
pre |
||||
+overflow-touch |
||||
overflow-x: auto |
||||
padding: $content-pre-padding |
||||
white-space: pre |
||||
word-wrap: normal |
||||
sup, |
||||
sub |
||||
font-size: 75% |
||||
table |
||||
width: 100% |
||||
td, |
||||
th |
||||
border: $content-table-cell-border |
||||
border-width: $content-table-cell-border-width |
||||
padding: $content-table-cell-padding |
||||
vertical-align: top |
||||
th |
||||
color: $content-table-cell-heading-color |
||||
&:not([align]) |
||||
text-align: inherit |
||||
thead |
||||
td, |
||||
th |
||||
border-width: $content-table-head-cell-border-width |
||||
color: $content-table-head-cell-color |
||||
tfoot |
||||
td, |
||||
th |
||||
border-width: $content-table-foot-cell-border-width |
||||
color: $content-table-foot-cell-color |
||||
tbody |
||||
tr |
||||
&:last-child |
||||
td, |
||||
th |
||||
border-bottom-width: 0 |
||||
.tabs |
||||
li + li |
||||
margin-top: 0 |
||||
// Sizes |
||||
&.is-small |
||||
font-size: $size-small |
||||
&.is-medium |
||||
font-size: $size-medium |
||||
&.is-large |
||||
font-size: $size-large |
@ -0,0 +1 @@ |
||||
@warn "The form.sass file is DEPRECATED. It has moved into its own /form folder. Please import sass/form/_all instead." |
@ -0,0 +1,21 @@ |
||||
$icon-dimensions: 1.5rem !default |
||||
$icon-dimensions-small: 1rem !default |
||||
$icon-dimensions-medium: 2rem !default |
||||
$icon-dimensions-large: 3rem !default |
||||
|
||||
.icon |
||||
align-items: center |
||||
display: inline-flex |
||||
justify-content: center |
||||
height: $icon-dimensions |
||||
width: $icon-dimensions |
||||
// Sizes |
||||
&.is-small |
||||
height: $icon-dimensions-small |
||||
width: $icon-dimensions-small |
||||
&.is-medium |
||||
height: $icon-dimensions-medium |
||||
width: $icon-dimensions-medium |
||||
&.is-large |
||||
height: $icon-dimensions-large |
||||
width: $icon-dimensions-large |
@ -0,0 +1,71 @@ |
||||
$dimensions: 16 24 32 48 64 96 128 !default |
||||
|
||||
.image |
||||
display: block |
||||
position: relative |
||||
img |
||||
display: block |
||||
height: auto |
||||
width: 100% |
||||
&.is-rounded |
||||
border-radius: $radius-rounded |
||||
&.is-fullwidth |
||||
width: 100% |
||||
// Ratio |
||||
&.is-square, |
||||
&.is-1by1, |
||||
&.is-5by4, |
||||
&.is-4by3, |
||||
&.is-3by2, |
||||
&.is-5by3, |
||||
&.is-16by9, |
||||
&.is-2by1, |
||||
&.is-3by1, |
||||
&.is-4by5, |
||||
&.is-3by4, |
||||
&.is-2by3, |
||||
&.is-3by5, |
||||
&.is-9by16, |
||||
&.is-1by2, |
||||
&.is-1by3 |
||||
img, |
||||
.has-ratio |
||||
@extend %overlay |
||||
height: 100% |
||||
width: 100% |
||||
&.is-square, |
||||
&.is-1by1 |
||||
padding-top: 100% |
||||
&.is-5by4 |
||||
padding-top: 80% |
||||
&.is-4by3 |
||||
padding-top: 75% |
||||
&.is-3by2 |
||||
padding-top: 66.6666% |
||||
&.is-5by3 |
||||
padding-top: 60% |
||||
&.is-16by9 |
||||
padding-top: 56.25% |
||||
&.is-2by1 |
||||
padding-top: 50% |
||||
&.is-3by1 |
||||
padding-top: 33.3333% |
||||
&.is-4by5 |
||||
padding-top: 125% |
||||
&.is-3by4 |
||||
padding-top: 133.3333% |
||||
&.is-2by3 |
||||
padding-top: 150% |
||||
&.is-3by5 |
||||
padding-top: 166.6666% |
||||
&.is-9by16 |
||||
padding-top: 177.7777% |
||||
&.is-1by2 |
||||
padding-top: 200% |
||||
&.is-1by3 |
||||
padding-top: 300% |
||||
// Sizes |
||||
@each $dimension in $dimensions |
||||
&.is-#{$dimension}x#{$dimension} |
||||
height: $dimension * 1px |
||||
width: $dimension * 1px |
@ -0,0 +1,50 @@ |
||||
$notification-background-color: $background !default |
||||
$notification-code-background-color: $scheme-main !default |
||||
$notification-radius: $radius !default |
||||
$notification-padding: 1.25rem 2.5rem 1.25rem 1.5rem !default |
||||
$notification-padding-ltr: 1.25rem 2.5rem 1.25rem 1.5rem !default |
||||
$notification-padding-rtl: 1.25rem 1.5rem 1.25rem 2.5rem !default |
||||
|
||||
$notification-colors: $colors !default |
||||
|
||||
.notification |
||||
@extend %block |
||||
background-color: $notification-background-color |
||||
border-radius: $notification-radius |
||||
position: relative |
||||
+ltr |
||||
padding: $notification-padding-ltr |
||||
+rtl |
||||
padding: $notification-padding-rtl |
||||
a:not(.button):not(.dropdown-item) |
||||
color: currentColor |
||||
text-decoration: underline |
||||
strong |
||||
color: currentColor |
||||
code, |
||||
pre |
||||
background: $notification-code-background-color |
||||
pre code |
||||
background: transparent |
||||
& > .delete |
||||
+ltr-position(0.5rem) |
||||
position: absolute |
||||
top: 0.5rem |
||||
.title, |
||||
.subtitle, |
||||
.content |
||||
color: currentColor |
||||
// Colors |
||||
@each $name, $pair in $notification-colors |
||||
$color: nth($pair, 1) |
||||
$color-invert: nth($pair, 2) |
||||
&.is-#{$name} |
||||
background-color: $color |
||||
color: $color-invert |
||||
// If light and dark colors are provided |
||||
@if length($pair) >= 4 |
||||
$color-light: nth($pair, 3) |
||||
$color-dark: nth($pair, 4) |
||||
&.is-light |
||||
background-color: $color-light |
||||
color: $color-dark |
@ -0,0 +1,39 @@ |
||||
.block |
||||
@extend %block |
||||
|
||||
.delete |
||||
@extend %delete |
||||
|
||||
.heading |
||||
display: block |
||||
font-size: 11px |
||||
letter-spacing: 1px |
||||
margin-bottom: 5px |
||||
text-transform: uppercase |
||||
|
||||
.highlight |
||||
@extend %block |
||||
font-weight: $weight-normal |
||||
max-width: 100% |
||||
overflow: hidden |
||||
padding: 0 |
||||
pre |
||||
overflow: auto |
||||
max-width: 100% |
||||
|
||||
.loader |
||||
@extend %loader |
||||
|
||||
.number |
||||
align-items: center |
||||
background-color: $background |
||||
border-radius: $radius-rounded |
||||
display: inline-flex |
||||
font-size: $size-medium |
||||
height: 2em |
||||
justify-content: center |
||||
margin-right: 1.5rem |
||||
min-width: 2.5em |
||||
padding: 0.25rem 0.5rem |
||||
text-align: center |
||||
vertical-align: top |
@ -0,0 +1,71 @@ |
||||
$progress-bar-background-color: $border-light !default |
||||
$progress-value-background-color: $text !default |
||||
$progress-border-radius: $radius-rounded !default |
||||
|
||||
$progress-indeterminate-duration: 1.5s !default |
||||
|
||||
$progress-colors: $colors !default |
||||
|
||||
.progress |
||||
@extend %block |
||||
-moz-appearance: none |
||||
-webkit-appearance: none |
||||
border: none |
||||
border-radius: $progress-border-radius |
||||
display: block |
||||
height: $size-normal |
||||
overflow: hidden |
||||
padding: 0 |
||||
width: 100% |
||||
&::-webkit-progress-bar |
||||
background-color: $progress-bar-background-color |
||||
&::-webkit-progress-value |
||||
background-color: $progress-value-background-color |
||||
&::-moz-progress-bar |
||||
background-color: $progress-value-background-color |
||||
&::-ms-fill |
||||
background-color: $progress-value-background-color |
||||
border: none |
||||
// Colors |
||||
@each $name, $pair in $progress-colors |
||||
$color: nth($pair, 1) |
||||
&.is-#{$name} |
||||
&::-webkit-progress-value |
||||
background-color: $color |
||||
&::-moz-progress-bar |
||||
background-color: $color |
||||
&::-ms-fill |
||||
background-color: $color |
||||
&:indeterminate |
||||
background-image: linear-gradient(to right, $color 30%, $progress-bar-background-color 30%) |
||||
|
||||
&:indeterminate |
||||
animation-duration: $progress-indeterminate-duration |
||||
animation-iteration-count: infinite |
||||
animation-name: moveIndeterminate |
||||
animation-timing-function: linear |
||||
background-color: $progress-bar-background-color |
||||
background-image: linear-gradient(to right, $text 30%, $progress-bar-background-color 30%) |
||||
background-position: top left |
||||
background-repeat: no-repeat |
||||
background-size: 150% 150% |
||||
&::-webkit-progress-bar |
||||
background-color: transparent |
||||
&::-moz-progress-bar |
||||
background-color: transparent |
||||
&::-ms-fill |
||||
animation-name: none |
||||
|
||||
// Sizes |
||||
&.is-small |
||||
height: $size-small |
||||
&.is-medium |
||||
height: $size-medium |
||||
&.is-large |
||||
height: $size-large |
||||
|
||||
@keyframes moveIndeterminate |
||||
from |
||||
background-position: 200% 0 |
||||
to |
||||
background-position: -200% 0 |
@ -0,0 +1,131 @@ |
||||
$table-color: $text-strong !default |
||||
$table-background-color: $scheme-main !default |
||||
|
||||
$table-cell-border: 1px solid $border !default |
||||
$table-cell-border-width: 0 0 1px !default |
||||
$table-cell-padding: 0.5em 0.75em !default |
||||
$table-cell-heading-color: $text-strong !default |
||||
|
||||
$table-head-cell-border-width: 0 0 2px !default |
||||
$table-head-cell-color: $text-strong !default |
||||
$table-foot-cell-border-width: 2px 0 0 !default |
||||
$table-foot-cell-color: $text-strong !default |
||||
|
||||
$table-head-background-color: transparent !default |
||||
$table-body-background-color: transparent !default |
||||
$table-foot-background-color: transparent !default |
||||
|
||||
$table-row-hover-background-color: $scheme-main-bis !default |
||||
|
||||
$table-row-active-background-color: $primary !default |
||||
$table-row-active-color: $primary-invert !default |
||||
|
||||
$table-striped-row-even-background-color: $scheme-main-bis !default |
||||
$table-striped-row-even-hover-background-color: $scheme-main-ter !default |
||||
|
||||
$table-colors: $colors !default |
||||
|
||||
.table |
||||
@extend %block |
||||
background-color: $table-background-color |
||||
color: $table-color |
||||
td, |
||||
th |
||||
border: $table-cell-border |
||||
border-width: $table-cell-border-width |
||||
padding: $table-cell-padding |
||||
vertical-align: top |
||||
// Colors |
||||
@each $name, $pair in $table-colors |
||||
$color: nth($pair, 1) |
||||
$color-invert: nth($pair, 2) |
||||
&.is-#{$name} |
||||
background-color: $color |
||||
border-color: $color |
||||
color: $color-invert |
||||
// Modifiers |
||||
&.is-narrow |
||||
white-space: nowrap |
||||
width: 1% |
||||
&.is-selected |
||||
background-color: $table-row-active-background-color |
||||
color: $table-row-active-color |
||||
a, |
||||
strong |
||||
color: currentColor |
||||
&.is-vcentered |
||||
vertical-align: middle |
||||
th |
||||
color: $table-cell-heading-color |
||||
&:not([align]) |
||||
text-align: inherit |
||||
tr |
||||
&.is-selected |
||||
background-color: $table-row-active-background-color |
||||
color: $table-row-active-color |
||||
a, |
||||
strong |
||||
color: currentColor |
||||
td, |
||||
th |
||||
border-color: $table-row-active-color |
||||
color: currentColor |
||||
thead |
||||
background-color: $table-head-background-color |
||||
td, |
||||
th |
||||
border-width: $table-head-cell-border-width |
||||
color: $table-head-cell-color |
||||
tfoot |
||||
background-color: $table-foot-background-color |
||||
td, |
||||
th |
||||
border-width: $table-foot-cell-border-width |
||||
color: $table-foot-cell-color |
||||
tbody |
||||
background-color: $table-body-background-color |
||||
tr |
||||
&:last-child |
||||
td, |
||||
th |
||||
border-bottom-width: 0 |
||||
// Modifiers |
||||
&.is-bordered |
||||
td, |
||||
th |
||||
border-width: 1px |
||||
tr |
||||
&:last-child |
||||
td, |
||||
th |
||||
border-bottom-width: 1px |
||||
&.is-fullwidth |
||||
width: 100% |
||||
&.is-hoverable |
||||
tbody |
||||
tr:not(.is-selected) |
||||
&:hover |
||||
background-color: $table-row-hover-background-color |
||||
&.is-striped |
||||
tbody |
||||
tr:not(.is-selected) |
||||
&:hover |
||||
background-color: $table-row-hover-background-color |
||||
&:nth-child(even) |
||||
background-color: $table-striped-row-even-hover-background-color |
||||
&.is-narrow |
||||
td, |
||||
th |
||||
padding: 0.25em 0.5em |
||||
&.is-striped |
||||
tbody |
||||
tr:not(.is-selected) |
||||
&:nth-child(even) |
||||
background-color: $table-striped-row-even-background-color |
||||
|
||||
.table-container |
||||
@extend %block |
||||
+overflow-touch |
||||
overflow: auto |
||||
overflow-y: hidden |
||||
max-width: 100% |
@ -0,0 +1,138 @@ |
||||
$tag-background-color: $background !default |
||||
$tag-color: $text !default |
||||
$tag-radius: $radius !default |
||||
$tag-delete-margin: 1px !default |
||||
|
||||
$tag-colors: $colors !default |
||||
|
||||
.tags |
||||
align-items: center |
||||
display: flex |
||||
flex-wrap: wrap |
||||
justify-content: flex-start |
||||
.tag |
||||
margin-bottom: 0.5rem |
||||
&:not(:last-child) |
||||
+ltr-property("margin", 0.5rem) |
||||
&:last-child |
||||
margin-bottom: -0.5rem |
||||
&:not(:last-child) |
||||
margin-bottom: 1rem |
||||
// Sizes |
||||
&.are-medium |
||||
.tag:not(.is-normal):not(.is-large) |
||||
font-size: $size-normal |
||||
&.are-large |
||||
.tag:not(.is-normal):not(.is-medium) |
||||
font-size: $size-medium |
||||
&.is-centered |
||||
justify-content: center |
||||
.tag |
||||
margin-right: 0.25rem |
||||
margin-left: 0.25rem |
||||
&.is-right |
||||
justify-content: flex-end |
||||
.tag |
||||
&:not(:first-child) |
||||
margin-left: 0.5rem |
||||
&:not(:last-child) |
||||
margin-right: 0 |
||||
&.has-addons |
||||
.tag |
||||
+ltr-property("margin", 0) |
||||
&:not(:first-child) |
||||
+ltr-property("margin", 0, false) |
||||
+ltr |
||||
border-top-left-radius: 0 |
||||
border-bottom-left-radius: 0 |
||||
+rtl |
||||
border-top-right-radius: 0 |
||||
border-bottom-right-radius: 0 |
||||
&:not(:last-child) |
||||
+ltr |
||||
border-top-right-radius: 0 |
||||
border-bottom-right-radius: 0 |
||||
+rtl |
||||
border-top-left-radius: 0 |
||||
border-bottom-left-radius: 0 |
||||
|
||||
.tag:not(body) |
||||
align-items: center |
||||
background-color: $tag-background-color |
||||
border-radius: $tag-radius |
||||
color: $tag-color |
||||
display: inline-flex |
||||
font-size: $size-small |
||||
height: 2em |
||||
justify-content: center |
||||
line-height: 1.5 |
||||
padding-left: 0.75em |
||||
padding-right: 0.75em |
||||
white-space: nowrap |
||||
.delete |
||||
+ltr-property("margin", 0.25rem, false) |
||||
+ltr-property("margin", -0.375rem) |
||||
// Colors |
||||
@each $name, $pair in $tag-colors |
||||
$color: nth($pair, 1) |
||||
$color-invert: nth($pair, 2) |
||||
&.is-#{$name} |
||||
background-color: $color |
||||
color: $color-invert |
||||
// If a light and dark colors are provided |
||||
@if length($pair) > 3 |
||||
$color-light: nth($pair, 3) |
||||
$color-dark: nth($pair, 4) |
||||
&.is-light |
||||
background-color: $color-light |
||||
color: $color-dark |
||||
// Sizes |
||||
&.is-normal |
||||
font-size: $size-small |
||||
&.is-medium |
||||
font-size: $size-normal |
||||
&.is-large |
||||
font-size: $size-medium |
||||
.icon |
||||
&:first-child:not(:last-child) |
||||
+ltr-property("margin", -0.375em, false) |
||||
+ltr-property("margin", 0.1875em) |
||||
&:last-child:not(:first-child) |
||||
+ltr-property("margin", 0.1875em, false) |
||||
+ltr-property("margin", -0.375em) |
||||
&:first-child:last-child |
||||
+ltr-property("margin", -0.375em, false) |
||||
+ltr-property("margin", -0.375em) |
||||
// Modifiers |
||||
&.is-delete |
||||
+ltr-property("margin", $tag-delete-margin, false) |
||||
padding: 0 |
||||
position: relative |
||||
width: 2em |
||||
&::before, |
||||
&::after |
||||
background-color: currentColor |
||||
content: "" |
||||
display: block |
||||
left: 50% |
||||
position: absolute |
||||
top: 50% |
||||
transform: translateX(-50%) translateY(-50%) rotate(45deg) |
||||
transform-origin: center center |
||||
&::before |
||||
height: 1px |
||||
width: 50% |
||||
&::after |
||||
height: 50% |
||||
width: 1px |
||||
&:hover, |
||||
&:focus |
||||
background-color: darken($tag-background-color, 5%) |
||||
&:active |
||||
background-color: darken($tag-background-color, 10%) |
||||
&.is-rounded |
||||
border-radius: $radius-rounded |
||||
|
||||
a.tag |
||||
&:hover |
||||
text-decoration: underline |
@ -0,0 +1,70 @@ |
||||
$title-color: $text-strong !default |
||||
$title-family: false !default |
||||
$title-size: $size-3 !default |
||||
$title-weight: $weight-semibold !default |
||||
$title-line-height: 1.125 !default |
||||
$title-strong-color: inherit !default |
||||
$title-strong-weight: inherit !default |
||||
$title-sub-size: 0.75em !default |
||||
$title-sup-size: 0.75em !default |
||||
|
||||
$subtitle-color: $text !default |
||||
$subtitle-family: false !default |
||||
$subtitle-size: $size-5 !default |
||||
$subtitle-weight: $weight-normal !default |
||||
$subtitle-line-height: 1.25 !default |
||||
$subtitle-strong-color: $text-strong !default |
||||
$subtitle-strong-weight: $weight-semibold !default |
||||
$subtitle-negative-margin: -1.25rem !default |
||||
|
||||
.title, |
||||
.subtitle |
||||
@extend %block |
||||
word-break: break-word |
||||
em, |
||||
span |
||||
font-weight: inherit |
||||
sub |
||||
font-size: $title-sub-size |
||||
sup |
||||
font-size: $title-sup-size |
||||
.tag |
||||
vertical-align: middle |
||||
|
||||
.title |
||||
color: $title-color |
||||
@if $title-family |
||||
font-family: $title-family |
||||
font-size: $title-size |
||||
font-weight: $title-weight |
||||
line-height: $title-line-height |
||||
strong |
||||
color: $title-strong-color |
||||
font-weight: $title-strong-weight |
||||
& + .highlight |
||||
margin-top: -0.75rem |
||||
&:not(.is-spaced) + .subtitle |
||||
margin-top: $subtitle-negative-margin |
||||
// Sizes |
||||
@each $size in $sizes |
||||
$i: index($sizes, $size) |
||||
&.is-#{$i} |
||||
font-size: $size |
||||
|
||||
.subtitle |
||||
color: $subtitle-color |
||||
@if $subtitle-family |
||||
font-family: $subtitle-family |
||||
font-size: $subtitle-size |
||||
font-weight: $subtitle-weight |
||||
line-height: $subtitle-line-height |
||||
strong |
||||
color: $subtitle-strong-color |
||||
font-weight: $subtitle-strong-weight |
||||
&:not(.is-spaced) + .title |
||||
margin-top: $subtitle-negative-margin |
||||
// Sizes |
||||
@each $size in $sizes |
||||
$i: index($sizes, $size) |
||||
&.is-#{$i} |
||||
font-size: $size |
@ -0,0 +1,9 @@ |
||||
/* Bulma Form */ |
||||
@charset "utf-8" |
||||
|
||||
@import "shared.sass" |
||||
@import "input-textarea.sass" |
||||
@import "checkbox-radio.sass" |
||||
@import "select.sass" |
||||
@import "file.sass" |
||||
@import "tools.sass" |
@ -0,0 +1,22 @@ |
||||
%checkbox-radio |
||||
cursor: pointer |
||||
display: inline-block |
||||
line-height: 1.25 |
||||
position: relative |
||||
input |
||||
cursor: pointer |
||||
&:hover |
||||
color: $input-hover-color |
||||
&[disabled], |
||||
fieldset[disabled] &, |
||||
input[disabled] |
||||
color: $input-disabled-color |
||||
cursor: not-allowed |
||||
|
||||
.checkbox |
||||
@extend %checkbox-radio |
||||
|
||||
.radio |
||||
@extend %checkbox-radio |
||||
& + .radio |
||||
+ltr-property("margin", 0.5em, false) |
@ -0,0 +1,182 @@ |
||||
$file-border-color: $border !default |
||||
$file-radius: $radius !default |
||||
|
||||
$file-cta-background-color: $scheme-main-ter !default |
||||
$file-cta-color: $text !default |
||||
$file-cta-hover-color: $text-strong !default |
||||
$file-cta-active-color: $text-strong !default |
||||
|
||||
$file-name-border-color: $border !default |
||||
$file-name-border-style: solid !default |
||||
$file-name-border-width: 1px 1px 1px 0 !default |
||||
$file-name-max-width: 16em !default |
||||
|
||||
$file-colors: $form-colors !default |
||||
|
||||
.file |
||||
@extend %unselectable |
||||
align-items: stretch |
||||
display: flex |
||||
justify-content: flex-start |
||||
position: relative |
||||
// Colors |
||||
@each $name, $pair in $file-colors |
||||
$color: nth($pair, 1) |
||||
$color-invert: nth($pair, 2) |
||||
&.is-#{$name} |
||||
.file-cta |
||||
background-color: $color |
||||
border-color: transparent |
||||
color: $color-invert |
||||
&:hover, |
||||
&.is-hovered |
||||
.file-cta |
||||
background-color: bulmaDarken($color, 2.5%) |
||||
border-color: transparent |
||||
color: $color-invert |
||||
&:focus, |
||||
&.is-focused |
||||
.file-cta |
||||
border-color: transparent |
||||
box-shadow: 0 0 0.5em bulmaRgba($color, 0.25) |
||||
color: $color-invert |
||||
&:active, |
||||
&.is-active |
||||
.file-cta |
||||
background-color: bulmaDarken($color, 5%) |
||||
border-color: transparent |
||||
color: $color-invert |
||||
// Sizes |
||||
&.is-small |
||||
font-size: $size-small |
||||
&.is-medium |
||||
font-size: $size-medium |
||||
.file-icon |
||||
.fa |
||||
font-size: 21px |
||||
&.is-large |
||||
font-size: $size-large |
||||
.file-icon |
||||
.fa |
||||
font-size: 28px |
||||
// Modifiers |
||||
&.has-name |
||||
.file-cta |
||||
border-bottom-right-radius: 0 |
||||
border-top-right-radius: 0 |
||||
.file-name |
||||
border-bottom-left-radius: 0 |
||||
border-top-left-radius: 0 |
||||
&.is-empty |
||||
.file-cta |
||||
border-radius: $file-radius |
||||
.file-name |
||||
display: none |
||||
&.is-boxed |
||||
.file-label |
||||
flex-direction: column |
||||
.file-cta |
||||
flex-direction: column |
||||
height: auto |
||||
padding: 1em 3em |
||||
.file-name |
||||
border-width: 0 1px 1px |
||||
.file-icon |
||||
height: 1.5em |
||||
width: 1.5em |
||||
.fa |
||||
font-size: 21px |
||||
&.is-small |
||||
.file-icon .fa |
||||
font-size: 14px |
||||
&.is-medium |
||||
.file-icon .fa |
||||
font-size: 28px |
||||
&.is-large |
||||
.file-icon .fa |
||||
font-size: 35px |
||||
&.has-name |
||||
.file-cta |
||||
border-radius: $file-radius $file-radius 0 0 |
||||
.file-name |
||||
border-radius: 0 0 $file-radius $file-radius |
||||
border-width: 0 1px 1px |
||||
&.is-centered |
||||
justify-content: center |
||||
&.is-fullwidth |
||||
.file-label |
||||
width: 100% |
||||
.file-name |
||||
flex-grow: 1 |
||||
max-width: none |
||||
&.is-right |
||||
justify-content: flex-end |
||||
.file-cta |
||||
border-radius: 0 $file-radius $file-radius 0 |
||||
.file-name |
||||
border-radius: $file-radius 0 0 $file-radius |
||||
border-width: 1px 0 1px 1px |
||||
order: -1 |
||||
|
||||
.file-label |
||||
align-items: stretch |
||||
display: flex |
||||
cursor: pointer |
||||
justify-content: flex-start |
||||
overflow: hidden |
||||
position: relative |
||||
&:hover |
||||
.file-cta |
||||
background-color: bulmaDarken($file-cta-background-color, 2.5%) |
||||
color: $file-cta-hover-color |
||||
.file-name |
||||
border-color: bulmaDarken($file-name-border-color, 2.5%) |
||||
&:active |
||||
.file-cta |
||||
background-color: bulmaDarken($file-cta-background-color, 5%) |
||||
color: $file-cta-active-color |
||||
.file-name |
||||
border-color: bulmaDarken($file-name-border-color, 5%) |
||||
|
||||
.file-input |
||||
height: 100% |
||||
left: 0 |
||||
opacity: 0 |
||||
outline: none |
||||
position: absolute |
||||
top: 0 |
||||
width: 100% |
||||
|
||||
.file-cta, |
||||
.file-name |
||||
@extend %control |
||||
border-color: $file-border-color |
||||
border-radius: $file-radius |
||||
font-size: 1em |
||||
padding-left: 1em |
||||
padding-right: 1em |
||||
white-space: nowrap |
||||
|
||||
.file-cta |
||||
background-color: $file-cta-background-color |
||||
color: $file-cta-color |
||||
|
||||
.file-name |
||||
border-color: $file-name-border-color |
||||
border-style: $file-name-border-style |
||||
border-width: $file-name-border-width |
||||
display: block |
||||
max-width: $file-name-max-width |
||||
overflow: hidden |
||||
text-align: inherit |
||||
text-overflow: ellipsis |
||||
|
||||
.file-icon |
||||
align-items: center |
||||
display: flex |
||||
height: 1em |
||||
justify-content: center |
||||
+ltr-property("margin", 0.5em) |
||||
width: 1em |
||||
.fa |
||||
font-size: 14px |
@ -0,0 +1,66 @@ |
||||
$textarea-padding: $control-padding-horizontal !default |
||||
$textarea-max-height: 40em !default |
||||
$textarea-min-height: 8em !default |
||||
|
||||
$textarea-colors: $form-colors !default |
||||
|
||||
%input-textarea |
||||
@extend %input |
||||
box-shadow: $input-shadow |
||||
max-width: 100% |
||||
width: 100% |
||||
&[readonly] |
||||
box-shadow: none |
||||
// Colors |
||||
@each $name, $pair in $textarea-colors |
||||
$color: nth($pair, 1) |
||||
&.is-#{$name} |
||||
border-color: $color |
||||
&:focus, |
||||
&.is-focused, |
||||
&:active, |
||||
&.is-active |
||||
box-shadow: $input-focus-box-shadow-size bulmaRgba($color, 0.25) |
||||
// Sizes |
||||
&.is-small |
||||
+control-small |
||||
&.is-medium |
||||
+control-medium |
||||
&.is-large |
||||
+control-large |
||||
// Modifiers |
||||
&.is-fullwidth |
||||
display: block |
||||
width: 100% |
||||
&.is-inline |
||||
display: inline |
||||
width: auto |
||||
|
||||
.input |
||||
@extend %input-textarea |
||||
&.is-rounded |
||||
border-radius: $radius-rounded |
||||
padding-left: calc(#{$control-padding-horizontal} + 0.375em) |
||||
padding-right: calc(#{$control-padding-horizontal} + 0.375em) |
||||
&.is-static |
||||
background-color: transparent |
||||
border-color: transparent |
||||
box-shadow: none |
||||
padding-left: 0 |
||||
padding-right: 0 |
||||
|
||||
.textarea |
||||
@extend %input-textarea |
||||
display: block |
||||
max-width: 100% |
||||
min-width: 100% |
||||
padding: $textarea-padding |
||||
resize: vertical |
||||
&:not([rows]) |
||||
max-height: $textarea-max-height |
||||
min-height: $textarea-min-height |
||||
&[rows] |
||||
height: initial |
||||
// Modifiers |
||||
&.has-fixed-size |
||||
resize: none |
@ -0,0 +1,87 @@ |
||||
$select-colors: $form-colors !default |
||||
|
||||
.select |
||||
display: inline-block |
||||
max-width: 100% |
||||
position: relative |
||||
vertical-align: top |
||||
&:not(.is-multiple) |
||||
height: $input-height |
||||
&:not(.is-multiple):not(.is-loading) |
||||
&::after |
||||
@extend %arrow |
||||
border-color: $input-arrow |
||||
+ltr-position(1.125em) |
||||
z-index: 4 |
||||
&.is-rounded |
||||
select |
||||
border-radius: $radius-rounded |
||||
+ltr-property("padding", 1em, false) |
||||
select |
||||
@extend %input |
||||
cursor: pointer |
||||
display: block |
||||
font-size: 1em |
||||
max-width: 100% |
||||
outline: none |
||||
&::-ms-expand |
||||
display: none |
||||
&[disabled]:hover, |
||||
fieldset[disabled] &:hover |
||||
border-color: $input-disabled-border-color |
||||
&:not([multiple]) |
||||
+ltr-property("padding", 2.5em) |
||||
&[multiple] |
||||
height: auto |
||||
padding: 0 |
||||
option |
||||
padding: 0.5em 1em |
||||
// States |
||||
&:not(.is-multiple):not(.is-loading):hover |
||||
&::after |
||||
border-color: $input-hover-color |
||||
// Colors |
||||
@each $name, $pair in $select-colors |
||||
$color: nth($pair, 1) |
||||
&.is-#{$name} |
||||
&:not(:hover)::after |
||||
border-color: $color |
||||
select |
||||
border-color: $color |
||||
&:hover, |
||||
&.is-hovered |
||||
border-color: bulmaDarken($color, 5%) |
||||
&:focus, |
||||
&.is-focused, |
||||
&:active, |
||||
&.is-active |
||||
box-shadow: $input-focus-box-shadow-size bulmaRgba($color, 0.25) |
||||
// Sizes |
||||
&.is-small |
||||
+control-small |
||||
&.is-medium |
||||
+control-medium |
||||
&.is-large |
||||
+control-large |
||||
// Modifiers |
||||
&.is-disabled |
||||
&::after |
||||
border-color: $input-disabled-color |
||||
&.is-fullwidth |
||||
width: 100% |
||||
select |
||||
width: 100% |
||||
&.is-loading |
||||
&::after |
||||
@extend %loader |
||||
margin-top: 0 |
||||
position: absolute |
||||
+ltr-position(0.625em) |
||||
top: 0.625em |
||||
transform: none |
||||
&.is-small:after |
||||
font-size: $size-small |
||||
&.is-medium:after |
||||
font-size: $size-medium |
||||
&.is-large:after |
||||
font-size: $size-large |
@ -0,0 +1,57 @@ |
||||
$form-colors: $colors !default |
||||
|
||||
$input-color: $text-strong !default |
||||
$input-background-color: $scheme-main !default |
||||
$input-border-color: $border !default |
||||
$input-height: $control-height !default |
||||
$input-shadow: inset 0 0.0625em 0.125em rgba($scheme-invert, 0.05) !default |
||||
$input-placeholder-color: bulmaRgba($input-color, 0.3) !default |
||||
|
||||
$input-hover-color: $text-strong !default |
||||
$input-hover-border-color: $border-hover !default |
||||
|
||||
$input-focus-color: $text-strong !default |
||||
$input-focus-border-color: $link !default |
||||
$input-focus-box-shadow-size: 0 0 0 0.125em !default |
||||
$input-focus-box-shadow-color: bulmaRgba($link, 0.25) !default |
||||
|
||||
$input-disabled-color: $text-light !default |
||||
$input-disabled-background-color: $background !default |
||||
$input-disabled-border-color: $background !default |
||||
$input-disabled-placeholder-color: bulmaRgba($input-disabled-color, 0.3) !default |
||||
|
||||
$input-arrow: $link !default |
||||
|
||||
$input-icon-color: $border !default |
||||
$input-icon-active-color: $text !default |
||||
|
||||
$input-radius: $radius !default |
||||
|
||||
=input |
||||
@extend %control |
||||
background-color: $input-background-color |
||||
border-color: $input-border-color |
||||
border-radius: $input-radius |
||||
color: $input-color |
||||
+placeholder |
||||
color: $input-placeholder-color |
||||
&:hover, |
||||
&.is-hovered |
||||
border-color: $input-hover-border-color |
||||
&:focus, |
||||
&.is-focused, |
||||
&:active, |
||||
&.is-active |
||||
border-color: $input-focus-border-color |
||||
box-shadow: $input-focus-box-shadow-size $input-focus-box-shadow-color |
||||
&[disabled], |
||||
fieldset[disabled] & |
||||
background-color: $input-disabled-background-color |
||||
border-color: $input-disabled-border-color |
||||
box-shadow: none |
||||
color: $input-disabled-color |
||||
+placeholder |
||||
color: $input-disabled-placeholder-color |
||||
|
||||
%input |
||||
+input |
@ -0,0 +1,215 @@ |
||||
$label-color: $text-strong !default |
||||
$label-weight: $weight-bold !default |
||||
|
||||
$help-size: $size-small !default |
||||
|
||||
$label-colors: $form-colors !default |
||||
|
||||
.label |
||||
color: $label-color |
||||
display: block |
||||
font-size: $size-normal |
||||
font-weight: $label-weight |
||||
&:not(:last-child) |
||||
margin-bottom: 0.5em |
||||
// Sizes |
||||
&.is-small |
||||
font-size: $size-small |
||||
&.is-medium |
||||
font-size: $size-medium |
||||
&.is-large |
||||
font-size: $size-large |
||||
|
||||
.help |
||||
display: block |
||||
font-size: $help-size |
||||
margin-top: 0.25rem |
||||
@each $name, $pair in $label-colors |
||||
$color: nth($pair, 1) |
||||
&.is-#{$name} |
||||
color: $color |
||||
|
||||
// Containers |
||||
|
||||
.field |
||||
&:not(:last-child) |
||||
margin-bottom: 0.75rem |
||||
// Modifiers |
||||
&.has-addons |
||||
display: flex |
||||
justify-content: flex-start |
||||
.control |
||||
&:not(:last-child) |
||||
+ltr-property("margin", -1px) |
||||
&:not(:first-child):not(:last-child) |
||||
.button, |
||||
.input, |
||||
.select select |
||||
border-radius: 0 |
||||
&:first-child:not(:only-child) |
||||
.button, |
||||
.input, |
||||
.select select |
||||
+ltr |
||||
border-bottom-right-radius: 0 |
||||
border-top-right-radius: 0 |
||||
+rtl |
||||
border-bottom-left-radius: 0 |
||||
border-top-left-radius: 0 |
||||
&:last-child:not(:only-child) |
||||
.button, |
||||
.input, |
||||
.select select |
||||
+ltr |
||||
border-bottom-left-radius: 0 |
||||
border-top-left-radius: 0 |
||||
+rtl |
||||
border-bottom-right-radius: 0 |
||||
border-top-right-radius: 0 |
||||
.button, |
||||
.input, |
||||
.select select |
||||
&:not([disabled]) |
||||
&:hover, |
||||
&.is-hovered |
||||
z-index: 2 |
||||
&:focus, |
||||
&.is-focused, |
||||
&:active, |
||||
&.is-active |
||||
z-index: 3 |
||||
&:hover |
||||
z-index: 4 |
||||
&.is-expanded |
||||
flex-grow: 1 |
||||
flex-shrink: 1 |
||||
&.has-addons-centered |
||||
justify-content: center |
||||
&.has-addons-right |
||||
justify-content: flex-end |
||||
&.has-addons-fullwidth |
||||
.control |
||||
flex-grow: 1 |
||||
flex-shrink: 0 |
||||
&.is-grouped |
||||
display: flex |
||||
justify-content: flex-start |
||||
& > .control |
||||
flex-shrink: 0 |
||||
&:not(:last-child) |
||||
margin-bottom: 0 |
||||
+ltr-property("margin", 0.75rem) |
||||
&.is-expanded |
||||
flex-grow: 1 |
||||
flex-shrink: 1 |
||||
&.is-grouped-centered |
||||
justify-content: center |
||||
&.is-grouped-right |
||||
justify-content: flex-end |
||||
&.is-grouped-multiline |
||||
flex-wrap: wrap |
||||
& > .control |
||||
&:last-child, |
||||
&:not(:last-child) |
||||
margin-bottom: 0.75rem |
||||
&:last-child |
||||
margin-bottom: -0.75rem |
||||
&:not(:last-child) |
||||
margin-bottom: 0 |
||||
&.is-horizontal |
||||
+tablet |
||||
display: flex |
||||
|
||||
.field-label |
||||
.label |
||||
font-size: inherit |
||||
+mobile |
||||
margin-bottom: 0.5rem |
||||
+tablet |
||||
flex-basis: 0 |
||||
flex-grow: 1 |
||||
flex-shrink: 0 |
||||
+ltr-property("margin", 1.5rem) |
||||
text-align: right |
||||
&.is-small |
||||
font-size: $size-small |
||||
padding-top: 0.375em |
||||
&.is-normal |
||||
padding-top: 0.375em |
||||
&.is-medium |
||||
font-size: $size-medium |
||||
padding-top: 0.375em |
||||
&.is-large |
||||
font-size: $size-large |
||||
padding-top: 0.375em |
||||
|
||||
.field-body |
||||
.field .field |
||||
margin-bottom: 0 |
||||
+tablet |
||||
display: flex |
||||
flex-basis: 0 |
||||
flex-grow: 5 |
||||
flex-shrink: 1 |
||||
.field |
||||
margin-bottom: 0 |
||||
& > .field |
||||
flex-shrink: 1 |
||||
&:not(.is-narrow) |
||||
flex-grow: 1 |
||||
&:not(:last-child) |
||||
+ltr-property("margin", 0.75rem) |
||||
|
||||
.control |
||||
box-sizing: border-box |
||||
clear: both |
||||
font-size: $size-normal |
||||
position: relative |
||||
text-align: inherit |
||||
// Modifiers |
||||
&.has-icons-left, |
||||
&.has-icons-right |
||||
.input, |
||||
.select |
||||
&:focus |
||||
& ~ .icon |
||||
color: $input-icon-active-color |
||||
&.is-small ~ .icon |
||||
font-size: $size-small |
||||
&.is-medium ~ .icon |
||||
font-size: $size-medium |
||||
&.is-large ~ .icon |
||||
font-size: $size-large |
||||
.icon |
||||
color: $input-icon-color |
||||
height: $input-height |
||||
pointer-events: none |
||||
position: absolute |
||||
top: 0 |
||||
width: $input-height |
||||
z-index: 4 |
||||
&.has-icons-left |
||||
.input, |
||||
.select select |
||||
padding-left: $input-height |
||||
.icon.is-left |
||||
left: 0 |
||||
&.has-icons-right |
||||
.input, |
||||
.select select |
||||
padding-right: $input-height |
||||
.icon.is-right |
||||
right: 0 |
||||
&.is-loading |
||||
&::after |
||||
@extend %loader |
||||
position: absolute !important |
||||
+ltr-position(0.625em) |
||||
top: 0.625em |
||||
z-index: 4 |
||||
&.is-small:after |
||||
font-size: $size-small |
||||
&.is-medium:after |
||||
font-size: $size-medium |
||||
&.is-large:after |
||||
font-size: $size-large |
@ -0,0 +1,5 @@ |
||||
/* Bulma Grid */ |
||||
@charset "utf-8" |
||||
|
||||
@import "columns.sass" |
||||
@import "tiles.sass" |
@ -0,0 +1,504 @@ |
||||
$column-gap: 0.75rem !default |
||||
|
||||
.column |
||||
display: block |
||||
flex-basis: 0 |
||||
flex-grow: 1 |
||||
flex-shrink: 1 |
||||
padding: $column-gap |
||||
.columns.is-mobile > &.is-narrow |
||||
flex: none |
||||
.columns.is-mobile > &.is-full |
||||
flex: none |
||||
width: 100% |
||||
.columns.is-mobile > &.is-three-quarters |
||||
flex: none |
||||
width: 75% |
||||
.columns.is-mobile > &.is-two-thirds |
||||
flex: none |
||||
width: 66.6666% |
||||
.columns.is-mobile > &.is-half |
||||
flex: none |
||||
width: 50% |
||||
.columns.is-mobile > &.is-one-third |
||||
flex: none |
||||
width: 33.3333% |
||||
.columns.is-mobile > &.is-one-quarter |
||||
flex: none |
||||
width: 25% |
||||
.columns.is-mobile > &.is-one-fifth |
||||
flex: none |
||||
width: 20% |
||||
.columns.is-mobile > &.is-two-fifths |
||||
flex: none |
||||
width: 40% |
||||
.columns.is-mobile > &.is-three-fifths |
||||
flex: none |
||||
width: 60% |
||||
.columns.is-mobile > &.is-four-fifths |
||||
flex: none |
||||
width: 80% |
||||
.columns.is-mobile > &.is-offset-three-quarters |
||||
margin-left: 75% |
||||
.columns.is-mobile > &.is-offset-two-thirds |
||||
margin-left: 66.6666% |
||||
.columns.is-mobile > &.is-offset-half |
||||
margin-left: 50% |
||||
.columns.is-mobile > &.is-offset-one-third |
||||
margin-left: 33.3333% |
||||
.columns.is-mobile > &.is-offset-one-quarter |
||||
margin-left: 25% |
||||
.columns.is-mobile > &.is-offset-one-fifth |
||||
margin-left: 20% |
||||
.columns.is-mobile > &.is-offset-two-fifths |
||||
margin-left: 40% |
||||
.columns.is-mobile > &.is-offset-three-fifths |
||||
margin-left: 60% |
||||
.columns.is-mobile > &.is-offset-four-fifths |
||||
margin-left: 80% |
||||
@for $i from 0 through 12 |
||||
.columns.is-mobile > &.is-#{$i} |
||||
flex: none |
||||
width: percentage($i / 12) |
||||
.columns.is-mobile > &.is-offset-#{$i} |
||||
margin-left: percentage($i / 12) |
||||
+mobile |
||||
&.is-narrow-mobile |
||||
flex: none |
||||
&.is-full-mobile |
||||
flex: none |
||||
width: 100% |
||||
&.is-three-quarters-mobile |
||||
flex: none |
||||
width: 75% |
||||
&.is-two-thirds-mobile |
||||
flex: none |
||||
width: 66.6666% |
||||
&.is-half-mobile |
||||
flex: none |
||||
width: 50% |
||||
&.is-one-third-mobile |
||||
flex: none |
||||
width: 33.3333% |
||||
&.is-one-quarter-mobile |
||||
flex: none |
||||
width: 25% |
||||
&.is-one-fifth-mobile |
||||
flex: none |
||||
width: 20% |
||||
&.is-two-fifths-mobile |
||||
flex: none |
||||
width: 40% |
||||
&.is-three-fifths-mobile |
||||
flex: none |
||||
width: 60% |
||||
&.is-four-fifths-mobile |
||||
flex: none |
||||
width: 80% |
||||
&.is-offset-three-quarters-mobile |
||||
margin-left: 75% |
||||
&.is-offset-two-thirds-mobile |
||||
margin-left: 66.6666% |
||||
&.is-offset-half-mobile |
||||
margin-left: 50% |
||||
&.is-offset-one-third-mobile |
||||
margin-left: 33.3333% |
||||
&.is-offset-one-quarter-mobile |
||||
margin-left: 25% |
||||
&.is-offset-one-fifth-mobile |
||||
margin-left: 20% |
||||
&.is-offset-two-fifths-mobile |
||||
margin-left: 40% |
||||
&.is-offset-three-fifths-mobile |
||||
margin-left: 60% |
||||
&.is-offset-four-fifths-mobile |
||||
margin-left: 80% |
||||
@for $i from 0 through 12 |
||||
&.is-#{$i}-mobile |
||||
flex: none |
||||
width: percentage($i / 12) |
||||
&.is-offset-#{$i}-mobile |
||||
margin-left: percentage($i / 12) |
||||
+tablet |
||||
&.is-narrow, |
||||
&.is-narrow-tablet |
||||
flex: none |
||||
&.is-full, |
||||
&.is-full-tablet |
||||
flex: none |
||||
width: 100% |
||||
&.is-three-quarters, |
||||
&.is-three-quarters-tablet |
||||
flex: none |
||||
width: 75% |
||||
&.is-two-thirds, |
||||
&.is-two-thirds-tablet |
||||
flex: none |
||||
width: 66.6666% |
||||
&.is-half, |
||||
&.is-half-tablet |
||||
flex: none |
||||
width: 50% |
||||
&.is-one-third, |
||||
&.is-one-third-tablet |
||||
flex: none |
||||
width: 33.3333% |
||||
&.is-one-quarter, |
||||
&.is-one-quarter-tablet |
||||
flex: none |
||||
width: 25% |
||||
&.is-one-fifth, |
||||
&.is-one-fifth-tablet |
||||
flex: none |
||||
width: 20% |
||||
&.is-two-fifths, |
||||
&.is-two-fifths-tablet |
||||
flex: none |
||||
width: 40% |
||||
&.is-three-fifths, |
||||
&.is-three-fifths-tablet |
||||
flex: none |
||||
width: 60% |
||||
&.is-four-fifths, |
||||
&.is-four-fifths-tablet |
||||
flex: none |
||||
width: 80% |
||||
&.is-offset-three-quarters, |
||||
&.is-offset-three-quarters-tablet |
||||
margin-left: 75% |
||||
&.is-offset-two-thirds, |
||||
&.is-offset-two-thirds-tablet |
||||
margin-left: 66.6666% |
||||
&.is-offset-half, |
||||
&.is-offset-half-tablet |
||||
margin-left: 50% |
||||
&.is-offset-one-third, |
||||
&.is-offset-one-third-tablet |
||||
margin-left: 33.3333% |
||||
&.is-offset-one-quarter, |
||||
&.is-offset-one-quarter-tablet |
||||
margin-left: 25% |
||||
&.is-offset-one-fifth, |
||||
&.is-offset-one-fifth-tablet |
||||
margin-left: 20% |
||||
&.is-offset-two-fifths, |
||||
&.is-offset-two-fifths-tablet |
||||
margin-left: 40% |
||||
&.is-offset-three-fifths, |
||||
&.is-offset-three-fifths-tablet |
||||
margin-left: 60% |
||||
&.is-offset-four-fifths, |
||||
&.is-offset-four-fifths-tablet |
||||
margin-left: 80% |
||||
@for $i from 0 through 12 |
||||
&.is-#{$i}, |
||||
&.is-#{$i}-tablet |
||||
flex: none |
||||
width: percentage($i / 12) |
||||
&.is-offset-#{$i}, |
||||
&.is-offset-#{$i}-tablet |
||||
margin-left: percentage($i / 12) |
||||
+touch |
||||
&.is-narrow-touch |
||||
flex: none |
||||
&.is-full-touch |
||||
flex: none |
||||
width: 100% |
||||
&.is-three-quarters-touch |
||||
flex: none |
||||
width: 75% |
||||
&.is-two-thirds-touch |
||||
flex: none |
||||
width: 66.6666% |
||||
&.is-half-touch |
||||
flex: none |
||||
width: 50% |
||||
&.is-one-third-touch |
||||
flex: none |
||||
width: 33.3333% |
||||
&.is-one-quarter-touch |
||||
flex: none |
||||
width: 25% |
||||
&.is-one-fifth-touch |
||||
flex: none |
||||
width: 20% |
||||
&.is-two-fifths-touch |
||||
flex: none |
||||
width: 40% |
||||
&.is-three-fifths-touch |
||||
flex: none |
||||
width: 60% |
||||
&.is-four-fifths-touch |
||||
flex: none |
||||
width: 80% |
||||
&.is-offset-three-quarters-touch |
||||
margin-left: 75% |
||||
&.is-offset-two-thirds-touch |
||||
margin-left: 66.6666% |
||||
&.is-offset-half-touch |
||||
margin-left: 50% |
||||
&.is-offset-one-third-touch |
||||
margin-left: 33.3333% |
||||
&.is-offset-one-quarter-touch |
||||
margin-left: 25% |
||||
&.is-offset-one-fifth-touch |
||||
margin-left: 20% |
||||
&.is-offset-two-fifths-touch |
||||
margin-left: 40% |
||||
&.is-offset-three-fifths-touch |
||||
margin-left: 60% |
||||
&.is-offset-four-fifths-touch |
||||
margin-left: 80% |
||||
@for $i from 0 through 12 |
||||
&.is-#{$i}-touch |
||||
flex: none |
||||
width: percentage($i / 12) |
||||
&.is-offset-#{$i}-touch |
||||
margin-left: percentage($i / 12) |
||||
+desktop |
||||
&.is-narrow-desktop |
||||
flex: none |
||||
&.is-full-desktop |
||||
flex: none |
||||
width: 100% |
||||
&.is-three-quarters-desktop |
||||
flex: none |
||||
width: 75% |
||||
&.is-two-thirds-desktop |
||||
flex: none |
||||
width: 66.6666% |
||||
&.is-half-desktop |
||||
flex: none |
||||
width: 50% |
||||
&.is-one-third-desktop |
||||
flex: none |
||||
width: 33.3333% |
||||
&.is-one-quarter-desktop |
||||
flex: none |
||||
width: 25% |
||||
&.is-one-fifth-desktop |
||||
flex: none |
||||
width: 20% |
||||
&.is-two-fifths-desktop |
||||
flex: none |
||||
width: 40% |
||||
&.is-three-fifths-desktop |
||||
flex: none |
||||
width: 60% |
||||
&.is-four-fifths-desktop |
||||
flex: none |
||||
width: 80% |
||||
&.is-offset-three-quarters-desktop |
||||
margin-left: 75% |
||||
&.is-offset-two-thirds-desktop |
||||
margin-left: 66.6666% |
||||
&.is-offset-half-desktop |
||||
margin-left: 50% |
||||
&.is-offset-one-third-desktop |
||||
margin-left: 33.3333% |
||||
&.is-offset-one-quarter-desktop |
||||
margin-left: 25% |
||||
&.is-offset-one-fifth-desktop |
||||
margin-left: 20% |
||||
&.is-offset-two-fifths-desktop |
||||
margin-left: 40% |
||||
&.is-offset-three-fifths-desktop |
||||
margin-left: 60% |
||||
&.is-offset-four-fifths-desktop |
||||
margin-left: 80% |
||||
@for $i from 0 through 12 |
||||
&.is-#{$i}-desktop |
||||
flex: none |
||||
width: percentage($i / 12) |
||||
&.is-offset-#{$i}-desktop |
||||
margin-left: percentage($i / 12) |
||||
+widescreen |
||||
&.is-narrow-widescreen |
||||
flex: none |
||||
&.is-full-widescreen |
||||
flex: none |
||||
width: 100% |
||||
&.is-three-quarters-widescreen |
||||
flex: none |
||||
width: 75% |
||||
&.is-two-thirds-widescreen |
||||
flex: none |
||||
width: 66.6666% |
||||
&.is-half-widescreen |
||||
flex: none |
||||
width: 50% |
||||
&.is-one-third-widescreen |
||||
flex: none |
||||
width: 33.3333% |
||||
&.is-one-quarter-widescreen |
||||
flex: none |
||||
width: 25% |
||||
&.is-one-fifth-widescreen |
||||
flex: none |
||||
width: 20% |
||||
&.is-two-fifths-widescreen |
||||
flex: none |
||||
width: 40% |
||||
&.is-three-fifths-widescreen |
||||
flex: none |
||||
width: 60% |
||||
&.is-four-fifths-widescreen |
||||
flex: none |
||||
width: 80% |
||||
&.is-offset-three-quarters-widescreen |
||||
margin-left: 75% |
||||
&.is-offset-two-thirds-widescreen |
||||
margin-left: 66.6666% |
||||
&.is-offset-half-widescreen |
||||
margin-left: 50% |
||||
&.is-offset-one-third-widescreen |
||||
margin-left: 33.3333% |
||||
&.is-offset-one-quarter-widescreen |
||||
margin-left: 25% |
||||
&.is-offset-one-fifth-widescreen |
||||
margin-left: 20% |
||||
&.is-offset-two-fifths-widescreen |
||||
margin-left: 40% |
||||
&.is-offset-three-fifths-widescreen |
||||
margin-left: 60% |
||||
&.is-offset-four-fifths-widescreen |
||||
margin-left: 80% |
||||
@for $i from 0 through 12 |
||||
&.is-#{$i}-widescreen |
||||
flex: none |
||||
width: percentage($i / 12) |
||||
&.is-offset-#{$i}-widescreen |
||||
margin-left: percentage($i / 12) |
||||
+fullhd |
||||
&.is-narrow-fullhd |
||||
flex: none |
||||
&.is-full-fullhd |
||||
flex: none |
||||
width: 100% |
||||
&.is-three-quarters-fullhd |
||||
flex: none |
||||
width: 75% |
||||
&.is-two-thirds-fullhd |
||||
flex: none |
||||
width: 66.6666% |
||||
&.is-half-fullhd |
||||
flex: none |
||||
width: 50% |
||||
&.is-one-third-fullhd |
||||
flex: none |
||||
width: 33.3333% |
||||
&.is-one-quarter-fullhd |
||||
flex: none |
||||
width: 25% |
||||
&.is-one-fifth-fullhd |
||||
flex: none |
||||
width: 20% |
||||
&.is-two-fifths-fullhd |
||||
flex: none |
||||
width: 40% |
||||
&.is-three-fifths-fullhd |
||||
flex: none |
||||
width: 60% |
||||
&.is-four-fifths-fullhd |
||||
flex: none |
||||
width: 80% |
||||
&.is-offset-three-quarters-fullhd |
||||
margin-left: 75% |
||||
&.is-offset-two-thirds-fullhd |
||||
margin-left: 66.6666% |
||||
&.is-offset-half-fullhd |
||||
margin-left: 50% |
||||
&.is-offset-one-third-fullhd |
||||
margin-left: 33.3333% |
||||
&.is-offset-one-quarter-fullhd |
||||
margin-left: 25% |
||||
&.is-offset-one-fifth-fullhd |
||||
margin-left: 20% |
||||
&.is-offset-two-fifths-fullhd |
||||
margin-left: 40% |
||||
&.is-offset-three-fifths-fullhd |
||||
margin-left: 60% |
||||
&.is-offset-four-fifths-fullhd |
||||
margin-left: 80% |
||||
@for $i from 0 through 12 |
||||
&.is-#{$i}-fullhd |
||||
flex: none |
||||
width: percentage($i / 12) |
||||
&.is-offset-#{$i}-fullhd |
||||
margin-left: percentage($i / 12) |
||||
|
||||
.columns |
||||
margin-left: (-$column-gap) |
||||
margin-right: (-$column-gap) |
||||
margin-top: (-$column-gap) |
||||
&:last-child |
||||
margin-bottom: (-$column-gap) |
||||
&:not(:last-child) |
||||
margin-bottom: calc(1.5rem - #{$column-gap}) |
||||
// Modifiers |
||||
&.is-centered |
||||
justify-content: center |
||||
&.is-gapless |
||||
margin-left: 0 |
||||
margin-right: 0 |
||||
margin-top: 0 |
||||
& > .column |
||||
margin: 0 |
||||
padding: 0 !important |
||||
&:not(:last-child) |
||||
margin-bottom: 1.5rem |
||||
&:last-child |
||||
margin-bottom: 0 |
||||
&.is-mobile |
||||
display: flex |
||||
&.is-multiline |
||||
flex-wrap: wrap |
||||
&.is-vcentered |
||||
align-items: center |
||||
// Responsiveness |
||||
+tablet |
||||
&:not(.is-desktop) |
||||
display: flex |
||||
+desktop |
||||
// Modifiers |
||||
&.is-desktop |
||||
display: flex |
||||
|
||||
@if $variable-columns |
||||
.columns.is-variable |
||||
--columnGap: 0.75rem |
||||
margin-left: calc(-1 * var(--columnGap)) |
||||
margin-right: calc(-1 * var(--columnGap)) |
||||
.column |
||||
padding-left: var(--columnGap) |
||||
padding-right: var(--columnGap) |
||||
@for $i from 0 through 8 |
||||
&.is-#{$i} |
||||
--columnGap: #{$i * 0.25rem} |
||||
+mobile |
||||
&.is-#{$i}-mobile |
||||
--columnGap: #{$i * 0.25rem} |
||||
+tablet |
||||
&.is-#{$i}-tablet |
||||
--columnGap: #{$i * 0.25rem} |
||||
+tablet-only |
||||
&.is-#{$i}-tablet-only |
||||
--columnGap: #{$i * 0.25rem} |
||||
+touch |
||||
&.is-#{$i}-touch |
||||
--columnGap: #{$i * 0.25rem} |
||||
+desktop |
||||
&.is-#{$i}-desktop |
||||
--columnGap: #{$i * 0.25rem} |
||||
+desktop-only |
||||
&.is-#{$i}-desktop-only |
||||
--columnGap: #{$i * 0.25rem} |
||||
+widescreen |
||||
&.is-#{$i}-widescreen |
||||
--columnGap: #{$i * 0.25rem} |
||||
+widescreen-only |
||||
&.is-#{$i}-widescreen-only |
||||
--columnGap: #{$i * 0.25rem} |
||||
+fullhd |
||||
&.is-#{$i}-fullhd |
||||
--columnGap: #{$i * 0.25rem} |
@ -0,0 +1,34 @@ |
||||
$tile-spacing: 0.75rem !default |
||||
|
||||
.tile |
||||
align-items: stretch |
||||
display: block |
||||
flex-basis: 0 |
||||
flex-grow: 1 |
||||
flex-shrink: 1 |
||||
min-height: min-content |
||||
// Modifiers |
||||
&.is-ancestor |
||||
margin-left: $tile-spacing * -1 |
||||
margin-right: $tile-spacing * -1 |
||||
margin-top: $tile-spacing * -1 |
||||
&:last-child |
||||
margin-bottom: $tile-spacing * -1 |
||||
&:not(:last-child) |
||||
margin-bottom: $tile-spacing |
||||
&.is-child |
||||
margin: 0 !important |
||||
&.is-parent |
||||
padding: $tile-spacing |
||||
&.is-vertical |
||||
flex-direction: column |
||||
& > .tile.is-child:not(:last-child) |
||||
margin-bottom: 1.5rem !important |
||||
// Responsiveness |
||||
+tablet |
||||
&:not(.is-child) |
||||
display: flex |
||||
@for $i from 1 through 12 |
||||
&.is-#{$i} |
||||
flex: none |
||||
width: ($i / 12) * 100% |
@ -0,0 +1,12 @@ |
||||
/* Bulma Helpers */ |
||||
@charset "utf-8" |
||||
|
||||
@import "color.sass" |
||||
@import "flexbox.sass" |
||||
@import "float.sass" |
||||
@import "other.sass" |
||||
@import "overflow.sass" |
||||
@import "position.sass" |
||||
@import "spacing.sass" |
||||
@import "typography.sass" |
||||
@import "visibility.sass" |
@ -0,0 +1,37 @@ |
||||
@each $name, $pair in $colors |
||||
$color: nth($pair, 1) |
||||
.has-text-#{$name} |
||||
color: $color !important |
||||
a.has-text-#{$name} |
||||
&:hover, |
||||
&:focus |
||||
color: bulmaDarken($color, 10%) !important |
||||
.has-background-#{$name} |
||||
background-color: $color !important |
||||
@if length($pair) >= 4 |
||||
$color-light: nth($pair, 3) |
||||
$color-dark: nth($pair, 4) |
||||
// Light |
||||
.has-text-#{$name}-light |
||||
color: $color-light !important |
||||
a.has-text-#{$name}-light |
||||
&:hover, |
||||
&:focus |
||||
color: bulmaDarken($color-light, 10%) !important |
||||
.has-background-#{$name}-light |
||||
background-color: $color-light !important |
||||
// Dark |
||||
.has-text-#{$name}-dark |
||||
color: $color-dark !important |
||||
a.has-text-#{$name}-dark |
||||
&:hover, |
||||
&:focus |
||||
color: bulmaLighten($color-dark, 10%) !important |
||||
.has-background-#{$name}-dark |
||||
background-color: $color-dark !important |
||||
|
||||
@each $name, $shade in $shades |
||||
.has-text-#{$name} |
||||
color: $shade !important |
||||
.has-background-#{$name} |
||||
background-color: $shade !important |
@ -0,0 +1,35 @@ |
||||
$flex-direction-values: row, row-reverse, column, column-reverse |
||||
@each $value in $flex-direction-values |
||||
.is-flex-direction-#{$value} |
||||
flex-direction: $value !important |
||||
|
||||
$flex-wrap-values: nowrap, wrap, wrap-reverse |
||||
@each $value in $flex-wrap-values |
||||
.is-flex-wrap-#{$value} |
||||
flex-wrap: $value !important |
||||
|
||||
$justify-content-values: flex-start, flex-end, center, space-between, space-around, space-evenly, start, end, left, right |
||||
@each $value in $justify-content-values |
||||
.is-justify-content-#{$value} |
||||
justify-content: $value !important |
||||
|
||||
$align-content-values: flex-start, flex-end, center, space-between, space-around, space-evenly, stretch, start, end, baseline |
||||
@each $value in $align-content-values |
||||
.is-align-content-#{$value} |
||||
align-content: $value !important |
||||
|
||||
$align-items-values: stretch, flex-start, flex-end, center, baseline, start, end, self-start, self-end |
||||
@each $value in $align-items-values |
||||
.is-align-items-#{$value} |
||||
align-items: $value !important |
||||
|
||||
$align-self-values: auto, flex-start, flex-end, center, baseline, stretch |
||||
@each $value in $align-self-values |
||||
.is-align-self-#{$value} |
||||
align-self: $value !important |
||||
|
||||
$flex-operators: grow, shrink |
||||
@each $operator in $flex-operators |
||||
@for $i from 0 through 5 |
||||
.is-flex-#{$operator}-#{$i} |
||||
flex-#{$operator}: $i !important |
@ -0,0 +1,8 @@ |
||||
.is-clearfix |
||||
+clearfix |
||||
|
||||
.is-pulled-left |
||||
float: left !important |
||||
|
||||
.is-pulled-right |
||||
float: right !important |
@ -0,0 +1,11 @@ |
||||
.is-radiusless |
||||
border-radius: 0 !important |
||||
|
||||
.is-shadowless |
||||
box-shadow: none !important |
||||
|
||||
.is-clickable |
||||
cursor: pointer !important |
||||
|
||||
.is-unselectable |
||||
@extend %unselectable |
@ -0,0 +1,2 @@ |
||||
.is-clipped |
||||
overflow: hidden !important |
@ -0,0 +1,5 @@ |
||||
.is-overlay |
||||
@extend %overlay |
||||
|
||||
.is-relative |
||||
position: relative !important |
@ -0,0 +1,31 @@ |
||||
.is-marginless |
||||
margin: 0 !important |
||||
|
||||
.is-paddingless |
||||
padding: 0 !important |
||||
|
||||
$spacing-shortcuts: ("margin": "m", "padding": "p") !default |
||||
$spacing-directions: ("top": "t", "right": "r", "bottom": "b", "left": "l") !default |
||||
$spacing-horizontal: "x" !default |
||||
$spacing-vertical: "y" !default |
||||
$spacing-values: ("0": 0, "1": 0.25rem, "2": 0.5rem, "3": 0.75rem, "4": 1rem, "5": 1.5rem, "6": 3rem) !default |
||||
|
||||
@each $property, $shortcut in $spacing-shortcuts |
||||
@each $name, $value in $spacing-values |
||||
// All directions |
||||
.#{$shortcut}-#{$name} |
||||
#{$property}: $value !important |
||||
// Cardinal directions |
||||
@each $direction, $suffix in $spacing-directions |
||||
.#{$shortcut}#{$suffix}-#{$name} |
||||
#{$property}-#{$direction}: $value !important |
||||
// Horizontal axis |
||||
@if $spacing-horizontal != null |
||||
.#{$shortcut}#{$spacing-horizontal}-#{$name} |
||||
#{$property}-left: $value !important |
||||
#{$property}-right: $value !important |
||||
// Vertical axis |
||||
@if $spacing-vertical != null |
||||
.#{$shortcut}#{$spacing-vertical}-#{$name} |
||||
#{$property}-top: $value !important |
||||
#{$property}-bottom: $value !important |
@ -0,0 +1,98 @@ |
||||
=typography-size($target:'') |
||||
@each $size in $sizes |
||||
$i: index($sizes, $size) |
||||
.is-size-#{$i}#{if($target == '', '', '-' + $target)} |
||||
font-size: $size !important |
||||
|
||||
+typography-size() |
||||
|
||||
+mobile |
||||
+typography-size('mobile') |
||||
|
||||
+tablet |
||||
+typography-size('tablet') |
||||
|
||||
+touch |
||||
+typography-size('touch') |
||||
|
||||
+desktop |
||||
+typography-size('desktop') |
||||
|
||||
+widescreen |
||||
+typography-size('widescreen') |
||||
|
||||
+fullhd |
||||
+typography-size('fullhd') |
||||
|
||||
$alignments: ('centered': 'center', 'justified': 'justify', 'left': 'left', 'right': 'right') |
||||
|
||||
@each $alignment, $text-align in $alignments |
||||
.has-text-#{$alignment} |
||||
text-align: #{$text-align} !important |
||||
|
||||
@each $alignment, $text-align in $alignments |
||||
+mobile |
||||
.has-text-#{$alignment}-mobile |
||||
text-align: #{$text-align} !important |
||||
+tablet |
||||
.has-text-#{$alignment}-tablet |
||||
text-align: #{$text-align} !important |
||||
+tablet-only |
||||
.has-text-#{$alignment}-tablet-only |
||||
text-align: #{$text-align} !important |
||||
+touch |
||||
.has-text-#{$alignment}-touch |
||||
text-align: #{$text-align} !important |
||||
+desktop |
||||
.has-text-#{$alignment}-desktop |
||||
text-align: #{$text-align} !important |
||||
+desktop-only |
||||
.has-text-#{$alignment}-desktop-only |
||||
text-align: #{$text-align} !important |
||||
+widescreen |
||||
.has-text-#{$alignment}-widescreen |
||||
text-align: #{$text-align} !important |
||||
+widescreen-only |
||||
.has-text-#{$alignment}-widescreen-only |
||||
text-align: #{$text-align} !important |
||||
+fullhd |
||||
.has-text-#{$alignment}-fullhd |
||||
text-align: #{$text-align} !important |
||||
|
||||
.is-capitalized |
||||
text-transform: capitalize !important |
||||
|
||||
.is-lowercase |
||||
text-transform: lowercase !important |
||||
|
||||
.is-uppercase |
||||
text-transform: uppercase !important |
||||
|
||||
.is-italic |
||||
font-style: italic !important |
||||
|
||||
.has-text-weight-light |
||||
font-weight: $weight-light !important |
||||
.has-text-weight-normal |
||||
font-weight: $weight-normal !important |
||||
.has-text-weight-medium |
||||
font-weight: $weight-medium !important |
||||
.has-text-weight-semibold |
||||
font-weight: $weight-semibold !important |
||||
.has-text-weight-bold |
||||
font-weight: $weight-bold !important |
||||
|
||||
.is-family-primary |
||||
font-family: $family-primary !important |
||||
|
||||
.is-family-secondary |
||||
font-family: $family-secondary !important |
||||
|
||||
.is-family-sans-serif |
||||
font-family: $family-sans-serif !important |
||||
|
||||
.is-family-monospace |
||||
font-family: $family-monospace !important |
||||
|
||||
.is-family-code |
||||
font-family: $family-code !important |
@ -0,0 +1,122 @@ |
||||
|
||||
|
||||
$displays: 'block' 'flex' 'inline' 'inline-block' 'inline-flex' |
||||
|
||||
@each $display in $displays |
||||
.is-#{$display} |
||||
display: #{$display} !important |
||||
+mobile |
||||
.is-#{$display}-mobile |
||||
display: #{$display} !important |
||||
+tablet |
||||
.is-#{$display}-tablet |
||||
display: #{$display} !important |
||||
+tablet-only |
||||
.is-#{$display}-tablet-only |
||||
display: #{$display} !important |
||||
+touch |
||||
.is-#{$display}-touch |
||||
display: #{$display} !important |
||||
+desktop |
||||
.is-#{$display}-desktop |
||||
display: #{$display} !important |
||||
+desktop-only |
||||
.is-#{$display}-desktop-only |
||||
display: #{$display} !important |
||||
+widescreen |
||||
.is-#{$display}-widescreen |
||||
display: #{$display} !important |
||||
+widescreen-only |
||||
.is-#{$display}-widescreen-only |
||||
display: #{$display} !important |
||||
+fullhd |
||||
.is-#{$display}-fullhd |
||||
display: #{$display} !important |
||||
|
||||
.is-hidden |
||||
display: none !important |
||||
|
||||
.is-sr-only |
||||
border: none !important |
||||
clip: rect(0, 0, 0, 0) !important |
||||
height: 0.01em !important |
||||
overflow: hidden !important |
||||
padding: 0 !important |
||||
position: absolute !important |
||||
white-space: nowrap !important |
||||
width: 0.01em !important |
||||
|
||||
+mobile |
||||
.is-hidden-mobile |
||||
display: none !important |
||||
|
||||
+tablet |
||||
.is-hidden-tablet |
||||
display: none !important |
||||
|
||||
+tablet-only |
||||
.is-hidden-tablet-only |
||||
display: none !important |
||||
|
||||
+touch |
||||
.is-hidden-touch |
||||
display: none !important |
||||
|
||||
+desktop |
||||
.is-hidden-desktop |
||||
display: none !important |
||||
|
||||
+desktop-only |
||||
.is-hidden-desktop-only |
||||
display: none !important |
||||
|
||||
+widescreen |
||||
.is-hidden-widescreen |
||||
display: none !important |
||||
|
||||
+widescreen-only |
||||
.is-hidden-widescreen-only |
||||
display: none !important |
||||
|
||||
+fullhd |
||||
.is-hidden-fullhd |
||||
display: none !important |
||||
|
||||
.is-invisible |
||||
visibility: hidden !important |
||||
|
||||
+mobile |
||||
.is-invisible-mobile |
||||
visibility: hidden !important |
||||
|
||||
+tablet |
||||
.is-invisible-tablet |
||||
visibility: hidden !important |
||||
|
||||
+tablet-only |
||||
.is-invisible-tablet-only |
||||
visibility: hidden !important |
||||
|
||||
+touch |
||||
.is-invisible-touch |
||||
visibility: hidden !important |
||||
|
||||
+desktop |
||||
.is-invisible-desktop |
||||
visibility: hidden !important |
||||
|
||||
+desktop-only |
||||
.is-invisible-desktop-only |
||||
visibility: hidden !important |
||||
|
||||
+widescreen |
||||
.is-invisible-widescreen |
||||
visibility: hidden !important |
||||
|
||||
+widescreen-only |
||||
.is-invisible-widescreen-only |
||||
visibility: hidden !important |
||||
|
||||
+fullhd |
||||
.is-invisible-fullhd |
||||
visibility: hidden !important |
@ -0,0 +1,6 @@ |
||||
/* Bulma Layout */ |
||||
@charset "utf-8" |
||||
|
||||
@import "hero.sass" |
||||
@import "section.sass" |
||||
@import "footer.sass" |
@ -0,0 +1,9 @@ |
||||
$footer-background-color: $scheme-main-bis !default |
||||
$footer-color: false !default |
||||
$footer-padding: 3rem 1.5rem 6rem !default |
||||
|
||||
.footer |
||||
background-color: $footer-background-color |
||||
padding: $footer-padding |
||||
@if $footer-color |
||||
color: $footer-color |
@ -0,0 +1,147 @@ |
||||
$hero-body-padding: 3rem 1.5rem !default |
||||
$hero-body-padding-small: 1.5rem !default |
||||
$hero-body-padding-medium: 9rem 1.5rem !default |
||||
$hero-body-padding-large: 18rem 1.5rem !default |
||||
|
||||
$hero-colors: $colors !default |
||||
|
||||
// Main container |
||||
.hero |
||||
align-items: stretch |
||||
display: flex |
||||
flex-direction: column |
||||
justify-content: space-between |
||||
.navbar |
||||
background: none |
||||
.tabs |
||||
ul |
||||
border-bottom: none |
||||
// Colors |
||||
@each $name, $pair in $hero-colors |
||||
$color: nth($pair, 1) |
||||
$color-invert: nth($pair, 2) |
||||
&.is-#{$name} |
||||
background-color: $color |
||||
color: $color-invert |
||||
a:not(.button):not(.dropdown-item):not(.tag):not(.pagination-link.is-current), |
||||
strong |
||||
color: inherit |
||||
.title |
||||
color: $color-invert |
||||
.subtitle |
||||
color: bulmaRgba($color-invert, 0.9) |
||||
a:not(.button), |
||||
strong |
||||
color: $color-invert |
||||
.navbar-menu |
||||
+touch |
||||
background-color: $color |
||||
.navbar-item, |
||||
.navbar-link |
||||
color: bulmaRgba($color-invert, 0.7) |
||||
a.navbar-item, |
||||
.navbar-link |
||||
&:hover, |
||||
&.is-active |
||||
background-color: bulmaDarken($color, 5%) |
||||
color: $color-invert |
||||
.tabs |
||||
a |
||||
color: $color-invert |
||||
opacity: 0.9 |
||||
&:hover |
||||
opacity: 1 |
||||
li |
||||
&.is-active a |
||||
opacity: 1 |
||||
&.is-boxed, |
||||
&.is-toggle |
||||
a |
||||
color: $color-invert |
||||
&:hover |
||||
background-color: bulmaRgba($scheme-invert, 0.1) |
||||
li.is-active a |
||||
&, |
||||
&:hover |
||||
background-color: $color-invert |
||||
border-color: $color-invert |
||||
color: $color |
||||
// Modifiers |
||||
@if type-of($color) == 'color' |
||||
&.is-bold |
||||
$gradient-top-left: darken(saturate(adjust-hue($color, -10deg), 10%), 10%) |
||||
$gradient-bottom-right: lighten(saturate(adjust-hue($color, 10deg), 5%), 5%) |
||||
background-image: linear-gradient(141deg, $gradient-top-left 0%, $color 71%, $gradient-bottom-right 100%) |
||||
+mobile |
||||
.navbar-menu |
||||
background-image: linear-gradient(141deg, $gradient-top-left 0%, $color 71%, $gradient-bottom-right 100%) |
||||
// Sizes |
||||
&.is-small |
||||
.hero-body |
||||
padding: $hero-body-padding-small |
||||
&.is-medium |
||||
+tablet |
||||
.hero-body |
||||
padding: $hero-body-padding-medium |
||||
&.is-large |
||||
+tablet |
||||
.hero-body |
||||
padding: $hero-body-padding-large |
||||
&.is-halfheight, |
||||
&.is-fullheight, |
||||
&.is-fullheight-with-navbar |
||||
.hero-body |
||||
align-items: center |
||||
display: flex |
||||
& > .container |
||||
flex-grow: 1 |
||||
flex-shrink: 1 |
||||
&.is-halfheight |
||||
min-height: 50vh |
||||
&.is-fullheight |
||||
min-height: 100vh |
||||
|
||||
// Components |
||||
|
||||
.hero-video |
||||
@extend %overlay |
||||
overflow: hidden |
||||
video |
||||
left: 50% |
||||
min-height: 100% |
||||
min-width: 100% |
||||
position: absolute |
||||
top: 50% |
||||
transform: translate3d(-50%, -50%, 0) |
||||
// Modifiers |
||||
&.is-transparent |
||||
opacity: 0.3 |
||||
// Responsiveness |
||||
+mobile |
||||
display: none |
||||
|
||||
.hero-buttons |
||||
margin-top: 1.5rem |
||||
// Responsiveness |
||||
+mobile |
||||
.button |
||||
display: flex |
||||
&:not(:last-child) |
||||
margin-bottom: 0.75rem |
||||
+tablet |
||||
display: flex |
||||
justify-content: center |
||||
.button:not(:last-child) |
||||
+ltr-property("margin", 1.5rem) |
||||
|
||||
// Containers |
||||
|
||||
.hero-head, |
||||
.hero-foot |
||||
flex-grow: 0 |
||||
flex-shrink: 0 |
||||
|
||||
.hero-body |
||||
flex-grow: 1 |
||||
flex-shrink: 0 |
||||
padding: $hero-body-padding |
@ -0,0 +1,13 @@ |
||||
$section-padding: 3rem 1.5rem !default |
||||
$section-padding-medium: 9rem 1.5rem !default |
||||
$section-padding-large: 18rem 1.5rem !default |
||||
|
||||
.section |
||||
padding: $section-padding |
||||
// Responsiveness |
||||
+desktop |
||||
// Sizes |
||||
&.is-medium |
||||
padding: $section-padding-medium |
||||
&.is-large |
||||
padding: $section-padding-large |
@ -0,0 +1,9 @@ |
||||
/* Bulma Utilities */ |
||||
@charset "utf-8" |
||||
|
||||
@import "initial-variables.sass" |
||||
@import "functions.sass" |
||||
@import "derived-variables.sass" |
||||
@import "animations.sass" |
||||
@import "mixins.sass" |
||||
@import "controls.sass" |
@ -0,0 +1,5 @@ |
||||
@keyframes spinAround |
||||
from |
||||
transform: rotate(0deg) |
||||
to |
||||
transform: rotate(359deg) |
@ -0,0 +1,50 @@ |
||||
$control-radius: $radius !default |
||||
$control-radius-small: $radius-small !default |
||||
|
||||
$control-border-width: 1px !default |
||||
|
||||
$control-height: 2.5em !default |
||||
$control-line-height: 1.5 !default |
||||
|
||||
$control-padding-vertical: calc(0.5em - #{$control-border-width}) !default |
||||
$control-padding-horizontal: calc(0.75em - #{$control-border-width}) !default |
||||
|
||||
=control |
||||
-moz-appearance: none |
||||
-webkit-appearance: none |
||||
align-items: center |
||||
border: $control-border-width solid transparent |
||||
border-radius: $control-radius |
||||
box-shadow: none |
||||
display: inline-flex |
||||
font-size: $size-normal |
||||
height: $control-height |
||||
justify-content: flex-start |
||||
line-height: $control-line-height |
||||
padding-bottom: $control-padding-vertical |
||||
padding-left: $control-padding-horizontal |
||||
padding-right: $control-padding-horizontal |
||||
padding-top: $control-padding-vertical |
||||
position: relative |
||||
vertical-align: top |
||||
// States |
||||
&:focus, |
||||
&.is-focused, |
||||
&:active, |
||||
&.is-active |
||||
outline: none |
||||
&[disabled], |
||||
fieldset[disabled] & |
||||
cursor: not-allowed |
||||
|
||||
%control |
||||
+control |
||||
|
||||
// The controls sizes use mixins so they can be used at different breakpoints |
||||
=control-small |
||||
border-radius: $control-radius-small |
||||
font-size: $size-small |
||||
=control-medium |
||||
font-size: $size-medium |
||||
=control-large |
||||
font-size: $size-large |
@ -0,0 +1,107 @@ |
||||
$primary: $turquoise !default |
||||
|
||||
$info: $cyan !default |
||||
$success: $green !default |
||||
$warning: $yellow !default |
||||
$danger: $red !default |
||||
|
||||
$light: $white-ter !default |
||||
$dark: $grey-darker !default |
||||
|
||||
// Invert colors |
||||
|
||||
$orange-invert: findColorInvert($orange) !default |
||||
$yellow-invert: findColorInvert($yellow) !default |
||||
$green-invert: findColorInvert($green) !default |
||||
$turquoise-invert: findColorInvert($turquoise) !default |
||||
$cyan-invert: findColorInvert($cyan) !default |
||||
$blue-invert: findColorInvert($blue) !default |
||||
$purple-invert: findColorInvert($purple) !default |
||||
$red-invert: findColorInvert($red) !default |
||||
|
||||
$primary-invert: findColorInvert($primary) !default |
||||
$primary-light: findLightColor($primary) !default |
||||
$primary-dark: findDarkColor($primary) !default |
||||
$info-invert: findColorInvert($info) !default |
||||
$info-light: findLightColor($info) !default |
||||
$info-dark: findDarkColor($info) !default |
||||
$success-invert: findColorInvert($success) !default |
||||
$success-light: findLightColor($success) !default |
||||
$success-dark: findDarkColor($success) !default |
||||
$warning-invert: findColorInvert($warning) !default |
||||
$warning-light: findLightColor($warning) !default |
||||
$warning-dark: findDarkColor($warning) !default |
||||
$danger-invert: findColorInvert($danger) !default |
||||
$danger-light: findLightColor($danger) !default |
||||
$danger-dark: findDarkColor($danger) !default |
||||
$light-invert: findColorInvert($light) !default |
||||
$dark-invert: findColorInvert($dark) !default |
||||
|
||||
// General colors |
||||
|
||||
$scheme-main: $white !default |
||||
$scheme-main-bis: $white-bis !default |
||||
$scheme-main-ter: $white-ter !default |
||||
$scheme-invert: $black !default |
||||
$scheme-invert-bis: $black-bis !default |
||||
$scheme-invert-ter: $black-ter !default |
||||
|
||||
$background: $white-ter !default |
||||
|
||||
$border: $grey-lighter !default |
||||
$border-hover: $grey-light !default |
||||
$border-light: $grey-lightest !default |
||||
$border-light-hover: $grey-light !default |
||||
|
||||
// Text colors |
||||
|
||||
$text: $grey-dark !default |
||||
$text-invert: findColorInvert($text) !default |
||||
$text-light: $grey !default |
||||
$text-strong: $grey-darker !default |
||||
|
||||
// Code colors |
||||
|
||||
$code: darken($red, 15%) !default |
||||
$code-background: $background !default |
||||
|
||||
$pre: $text !default |
||||
$pre-background: $background !default |
||||
|
||||
// Link colors |
||||
|
||||
$link: $blue !default |
||||
$link-invert: findColorInvert($link) !default |
||||
$link-light: findLightColor($link) !default |
||||
$link-dark: findDarkColor($link) !default |
||||
$link-visited: $purple !default |
||||
|
||||
$link-hover: $grey-darker !default |
||||
$link-hover-border: $grey-light !default |
||||
|
||||
$link-focus: $grey-darker !default |
||||
$link-focus-border: $blue !default |
||||
|
||||
$link-active: $grey-darker !default |
||||
$link-active-border: $grey-dark !default |
||||
|
||||
// Typography |
||||
|
||||
$family-primary: $family-sans-serif !default |
||||
$family-secondary: $family-sans-serif !default |
||||
$family-code: $family-monospace !default |
||||
|
||||
$size-small: $size-7 !default |
||||
$size-normal: $size-6 !default |
||||
$size-medium: $size-5 !default |
||||
$size-large: $size-4 !default |
||||
|
||||
// Lists and maps |
||||
$custom-colors: null !default |
||||
$custom-shades: null !default |
||||
|
||||
$colors: mergeColorMaps(("white": ($white, $black), "black": ($black, $white), "light": ($light, $light-invert), "dark": ($dark, $dark-invert), "primary": ($primary, $primary-invert, $primary-light, $primary-dark), "link": ($link, $link-invert, $link-light, $link-dark), "info": ($info, $info-invert, $info-light, $info-dark), "success": ($success, $success-invert, $success-light, $success-dark), "warning": ($warning, $warning-invert, $warning-light, $warning-dark), "danger": ($danger, $danger-invert, $danger-light, $danger-dark)), $custom-colors) !default |
||||
|
||||
$shades: mergeColorMaps(("black-bis": $black-bis, "black-ter": $black-ter, "grey-darker": $grey-darker, "grey-dark": $grey-dark, "grey": $grey, "grey-light": $grey-light, "grey-lighter": $grey-lighter, "white-ter": $white-ter, "white-bis": $white-bis), $custom-shades) !default |
||||
|
||||
$sizes: $size-1 $size-2 $size-3 $size-4 $size-5 $size-6 $size-7 !default |
@ -0,0 +1,115 @@ |
||||
@function mergeColorMaps($bulma-colors, $custom-colors) |
||||
// We return at least Bulma's hard-coded colors |
||||
$merged-colors: $bulma-colors |
||||
|
||||
// We want a map as input |
||||
@if type-of($custom-colors) == 'map' |
||||
@each $name, $components in $custom-colors |
||||
// The color name should be a string |
||||
// and the components either a single color |
||||
// or a colors list with at least one element |
||||
@if type-of($name) == 'string' and (type-of($components) == 'list' or type-of($components) == 'color') and length($components) >= 1 |
||||
$color-base: null |
||||
$color-invert: null |
||||
$color-light: null |
||||
$color-dark: null |
||||
$value: null |
||||
|
||||
// The param can either be a single color |
||||
// or a list of 2 colors |
||||
@if type-of($components) == 'color' |
||||
$color-base: $components |
||||
$color-invert: findColorInvert($color-base) |
||||
$color-light: findLightColor($color-base) |
||||
$color-dark: findDarkColor($color-base) |
||||
@else if type-of($components) == 'list' |
||||
$color-base: nth($components, 1) |
||||
// If Invert, Light and Dark are provided |
||||
@if length($components) > 3 |
||||
$color-invert: nth($components, 2) |
||||
$color-light: nth($components, 3) |
||||
$color-dark: nth($components, 4) |
||||
// If only Invert and Light are provided |
||||
@else if length($components) > 2 |
||||
$color-invert: nth($components, 2) |
||||
$color-light: nth($components, 3) |
||||
$color-dark: findDarkColor($color-base) |
||||
// If only Invert is provided |
||||
@else |
||||
$color-invert: nth($components, 2) |
||||
$color-light: findLightColor($color-base) |
||||
$color-dark: findDarkColor($color-base) |
||||
|
||||
$value: ($color-base, $color-invert, $color-light, $color-dark) |
||||
|
||||
// We only want to merge the map if the color base is an actual color |
||||
@if type-of($color-base) == 'color' |
||||
// We merge this colors elements as map with Bulma's colors map |
||||
// (we can override them this way, no multiple definition for the same name) |
||||
// $merged-colors: map_merge($merged-colors, ($name: ($color-base, $color-invert, $color-light, $color-dark))) |
||||
$merged-colors: map_merge($merged-colors, ($name: $value)) |
||||
|
||||
@return $merged-colors |
||||
|
||||
@function powerNumber($number, $exp) |
||||
$value: 1 |
||||
@if $exp > 0 |
||||
@for $i from 1 through $exp |
||||
$value: $value * $number |
||||
@else if $exp < 0 |
||||
@for $i from 1 through -$exp |
||||
$value: $value / $number |
||||
@return $value |
||||
|
||||
@function colorLuminance($color) |
||||
@if type-of($color) != 'color' |
||||
@return 0.55 |
||||
$color-rgb: ('red': red($color),'green': green($color),'blue': blue($color)) |
||||
@each $name, $value in $color-rgb |
||||
$adjusted: 0 |
||||
$value: $value / 255 |
||||
@if $value < 0.03928 |
||||
$value: $value / 12.92 |
||||
@else |
||||
$value: ($value + .055) / 1.055 |
||||
$value: powerNumber($value, 2) |
||||
$color-rgb: map-merge($color-rgb, ($name: $value)) |
||||
@return (map-get($color-rgb, 'red') * .2126) + (map-get($color-rgb, 'green') * .7152) + (map-get($color-rgb, 'blue') * .0722) |
||||
|
||||
@function findColorInvert($color) |
||||
@if (colorLuminance($color) > 0.55) |
||||
@return rgba(#000, 0.7) |
||||
@else |
||||
@return #fff |
||||
|
||||
@function findLightColor($color) |
||||
@if type-of($color) == 'color' |
||||
$l: 96% |
||||
@if lightness($color) > 96% |
||||
$l: lightness($color) |
||||
@return change-color($color, $lightness: $l) |
||||
@return $background |
||||
|
||||
@function findDarkColor($color) |
||||
@if type-of($color) == 'color' |
||||
$base-l: 29% |
||||
$luminance: colorLuminance($color) |
||||
$luminance-delta: (0.53 - $luminance) |
||||
$target-l: round($base-l + ($luminance-delta * 53)) |
||||
@return change-color($color, $lightness: max($base-l, $target-l)) |
||||
@return $text-strong |
||||
|
||||
@function bulmaRgba($color, $alpha) |
||||
@if type-of($color) != 'color' |
||||
@return $color |
||||
@return rgba($color, $alpha) |
||||
|
||||
@function bulmaDarken($color, $amount) |
||||
@if type-of($color) != 'color' |
||||
@return $color |
||||
@return darken($color, $amount) |
||||
|
||||
@function bulmaLighten($color, $amount) |
||||
@if type-of($color) != 'color' |
||||
@return $color |
||||
@return lighten($color, $amount) |
@ -0,0 +1,78 @@ |
||||
// Colors |
||||
|
||||
$black: hsl(0, 0%, 4%) !default |
||||
$black-bis: hsl(0, 0%, 7%) !default |
||||
$black-ter: hsl(0, 0%, 14%) !default |
||||
|
||||
$grey-darker: hsl(0, 0%, 21%) !default |
||||
$grey-dark: hsl(0, 0%, 29%) !default |
||||
$grey: hsl(0, 0%, 48%) !default |
||||
$grey-light: hsl(0, 0%, 71%) !default |
||||
$grey-lighter: hsl(0, 0%, 86%) !default |
||||
$grey-lightest: hsl(0, 0%, 93%) !default |
||||
|
||||
$white-ter: hsl(0, 0%, 96%) !default |
||||
$white-bis: hsl(0, 0%, 98%) !default |
||||
$white: hsl(0, 0%, 100%) !default |
||||
|
||||
$orange: hsl(14, 100%, 53%) !default |
||||
$yellow: hsl(48, 100%, 67%) !default |
||||
$green: hsl(141, 53%, 53%) !default |
||||
$turquoise: hsl(171, 100%, 41%) !default |
||||
$cyan: hsl(204, 71%, 53%) !default |
||||
$blue: hsl(217, 71%, 53%) !default |
||||
$purple: hsl(271, 100%, 71%) !default |
||||
$red: hsl(348, 86%, 61%) !default |
||||
|
||||
// Typography |
||||
|
||||
$family-sans-serif: BlinkMacSystemFont, -apple-system, "Segoe UI", "Roboto", "Oxygen", "Ubuntu", "Cantarell", "Fira Sans", "Droid Sans", "Helvetica Neue", "Helvetica", "Arial", sans-serif !default |
||||
$family-monospace: monospace !default |
||||
$render-mode: optimizeLegibility !default |
||||
|
||||
$size-1: 3rem !default |
||||
$size-2: 2.5rem !default |
||||
$size-3: 2rem !default |
||||
$size-4: 1.5rem !default |
||||
$size-5: 1.25rem !default |
||||
$size-6: 1rem !default |
||||
$size-7: 0.75rem !default |
||||
|
||||
$weight-light: 300 !default |
||||
$weight-normal: 400 !default |
||||
$weight-medium: 500 !default |
||||
$weight-semibold: 600 !default |
||||
$weight-bold: 700 !default |
||||
|
||||
// Spacing |
||||
|
||||
$block-spacing: 1.5rem !default |
||||
|
||||
// Responsiveness |
||||
|
||||
// The container horizontal gap, which acts as the offset for breakpoints |
||||
$gap: 32px !default |
||||
// 960, 1152, and 1344 have been chosen because they are divisible by both 12 and 16 |
||||
$tablet: 769px !default |
||||
// 960px container + 4rem |
||||
$desktop: 960px + (2 * $gap) !default |
||||
// 1152px container + 4rem |
||||
$widescreen: 1152px + (2 * $gap) !default |
||||
$widescreen-enabled: true !default |
||||
// 1344px container + 4rem |
||||
$fullhd: 1344px + (2 * $gap) !default |
||||
$fullhd-enabled: true !default |
||||
|
||||
// Miscellaneous |
||||
|
||||
$easing: ease-out !default |
||||
$radius-small: 2px !default |
||||
$radius: 4px !default |
||||
$radius-large: 6px !default |
||||
$radius-rounded: 290486px !default |
||||
$speed: 86ms !default |
||||
|
||||
// Flags |
||||
|
||||
$variable-columns: true !default |
||||
$rtl: false !default |
@ -0,0 +1,285 @@ |
||||
@import "initial-variables" |
||||
|
||||
=clearfix |
||||
&::after |
||||
clear: both |
||||
content: " " |
||||
display: table |
||||
|
||||
=center($width, $height: 0) |
||||
position: absolute |
||||
@if $height != 0 |
||||
left: calc(50% - (#{$width} / 2)) |
||||
top: calc(50% - (#{$height} / 2)) |
||||
@else |
||||
left: calc(50% - (#{$width} / 2)) |
||||
top: calc(50% - (#{$width} / 2)) |
||||
|
||||
=fa($size, $dimensions) |
||||
display: inline-block |
||||
font-size: $size |
||||
height: $dimensions |
||||
line-height: $dimensions |
||||
text-align: center |
||||
vertical-align: top |
||||
width: $dimensions |
||||
|
||||
=hamburger($dimensions) |
||||
cursor: pointer |
||||
display: block |
||||
height: $dimensions |
||||
position: relative |
||||
width: $dimensions |
||||
span |
||||
background-color: currentColor |
||||
display: block |
||||
height: 1px |
||||
left: calc(50% - 8px) |
||||
position: absolute |
||||
transform-origin: center |
||||
transition-duration: $speed |
||||
transition-property: background-color, opacity, transform |
||||
transition-timing-function: $easing |
||||
width: 16px |
||||
&:nth-child(1) |
||||
top: calc(50% - 6px) |
||||
&:nth-child(2) |
||||
top: calc(50% - 1px) |
||||
&:nth-child(3) |
||||
top: calc(50% + 4px) |
||||
&:hover |
||||
background-color: bulmaRgba(black, 0.05) |
||||
// Modifers |
||||
&.is-active |
||||
span |
||||
&:nth-child(1) |
||||
transform: translateY(5px) rotate(45deg) |
||||
&:nth-child(2) |
||||
opacity: 0 |
||||
&:nth-child(3) |
||||
transform: translateY(-5px) rotate(-45deg) |
||||
|
||||
=overflow-touch |
||||
-webkit-overflow-scrolling: touch |
||||
|
||||
=placeholder |
||||
$placeholders: ':-moz' ':-webkit-input' '-moz' '-ms-input' |
||||
@each $placeholder in $placeholders |
||||
&:#{$placeholder}-placeholder |
||||
@content |
||||
|
||||
// Responsiveness |
||||
|
||||
=from($device) |
||||
@media screen and (min-width: $device) |
||||
@content |
||||
|
||||
=until($device) |
||||
@media screen and (max-width: $device - 1px) |
||||
@content |
||||
|
||||
=mobile |
||||
@media screen and (max-width: $tablet - 1px) |
||||
@content |
||||
|
||||
=tablet |
||||
@media screen and (min-width: $tablet), print |
||||
@content |
||||
|
||||
=tablet-only |
||||
@media screen and (min-width: $tablet) and (max-width: $desktop - 1px) |
||||
@content |
||||
|
||||
=touch |
||||
@media screen and (max-width: $desktop - 1px) |
||||
@content |
||||
|
||||
=desktop |
||||
@media screen and (min-width: $desktop) |
||||
@content |
||||
|
||||
=desktop-only |
||||
@if $widescreen-enabled |
||||
@media screen and (min-width: $desktop) and (max-width: $widescreen - 1px) |
||||
@content |
||||
|
||||
=until-widescreen |
||||
@if $widescreen-enabled |
||||
@media screen and (max-width: $widescreen - 1px) |
||||
@content |
||||
|
||||
=widescreen |
||||
@if $widescreen-enabled |
||||
@media screen and (min-width: $widescreen) |
||||
@content |
||||
|
||||
=widescreen-only |
||||
@if $widescreen-enabled and $fullhd-enabled |
||||
@media screen and (min-width: $widescreen) and (max-width: $fullhd - 1px) |
||||
@content |
||||
|
||||
=until-fullhd |
||||
@if $fullhd-enabled |
||||
@media screen and (max-width: $fullhd - 1px) |
||||
@content |
||||
|
||||
=fullhd |
||||
@if $fullhd-enabled |
||||
@media screen and (min-width: $fullhd) |
||||
@content |
||||
|
||||
=ltr |
||||
@if not $rtl |
||||
@content |
||||
|
||||
=rtl |
||||
@if $rtl |
||||
@content |
||||
|
||||
=ltr-property($property, $spacing, $right: true) |
||||
$normal: if($right, "right", "left") |
||||
$opposite: if($right, "left", "right") |
||||
@if $rtl |
||||
#{$property}-#{$opposite}: $spacing |
||||
@else |
||||
#{$property}-#{$normal}: $spacing |
||||
|
||||
=ltr-position($spacing, $right: true) |
||||
$normal: if($right, "right", "left") |
||||
$opposite: if($right, "left", "right") |
||||
@if $rtl |
||||
#{$opposite}: $spacing |
||||
@else |
||||
#{$normal}: $spacing |
||||
|
||||
// Placeholders |
||||
|
||||
=unselectable |
||||
-webkit-touch-callout: none |
||||
-webkit-user-select: none |
||||
-moz-user-select: none |
||||
-ms-user-select: none |
||||
user-select: none |
||||
|
||||
%unselectable |
||||
+unselectable |
||||
|
||||
=arrow($color: transparent) |
||||
border: 3px solid $color |
||||
border-radius: 2px |
||||
border-right: 0 |
||||
border-top: 0 |
||||
content: " " |
||||
display: block |
||||
height: 0.625em |
||||
margin-top: -0.4375em |
||||
pointer-events: none |
||||
position: absolute |
||||
top: 50% |
||||
transform: rotate(-45deg) |
||||
transform-origin: center |
||||
width: 0.625em |
||||
|
||||
%arrow |
||||
+arrow |
||||
|
||||
=block($spacing: $block-spacing) |
||||
&:not(:last-child) |
||||
margin-bottom: $spacing |
||||
|
||||
%block |
||||
+block |
||||
|
||||
=delete |
||||
@extend %unselectable |
||||
-moz-appearance: none |
||||
-webkit-appearance: none |
||||
background-color: bulmaRgba($scheme-invert, 0.2) |
||||
border: none |
||||
border-radius: $radius-rounded |
||||
cursor: pointer |
||||
pointer-events: auto |
||||
display: inline-block |
||||
flex-grow: 0 |
||||
flex-shrink: 0 |
||||
font-size: 0 |
||||
height: 20px |
||||
max-height: 20px |
||||
max-width: 20px |
||||
min-height: 20px |
||||
min-width: 20px |
||||
outline: none |
||||
position: relative |
||||
vertical-align: top |
||||
width: 20px |
||||
&::before, |
||||
&::after |
||||
background-color: $scheme-main |
||||
content: "" |
||||
display: block |
||||
left: 50% |
||||
position: absolute |
||||
top: 50% |
||||
transform: translateX(-50%) translateY(-50%) rotate(45deg) |
||||
transform-origin: center center |
||||
&::before |
||||
height: 2px |
||||
width: 50% |
||||
&::after |
||||
height: 50% |
||||
width: 2px |
||||
&:hover, |
||||
&:focus |
||||
background-color: bulmaRgba($scheme-invert, 0.3) |
||||
&:active |
||||
background-color: bulmaRgba($scheme-invert, 0.4) |
||||
// Sizes |
||||
&.is-small |
||||
height: 16px |
||||
max-height: 16px |
||||
max-width: 16px |
||||
min-height: 16px |
||||
min-width: 16px |
||||
width: 16px |
||||
&.is-medium |
||||
height: 24px |
||||
max-height: 24px |
||||
max-width: 24px |
||||
min-height: 24px |
||||
min-width: 24px |
||||
width: 24px |
||||
&.is-large |
||||
height: 32px |
||||
max-height: 32px |
||||
max-width: 32px |
||||
min-height: 32px |
||||
min-width: 32px |
||||
width: 32px |
||||
|
||||
%delete |
||||
+delete |
||||
|
||||
=loader |
||||
animation: spinAround 500ms infinite linear |
||||
border: 2px solid $grey-lighter |
||||
border-radius: $radius-rounded |
||||
border-right-color: transparent |
||||
border-top-color: transparent |
||||
content: "" |
||||
display: block |
||||
height: 1em |
||||
position: relative |
||||
width: 1em |
||||
|
||||
%loader |
||||
+loader |
||||
|
||||
=overlay($offset: 0) |
||||
bottom: $offset |
||||
left: $offset |
||||
position: absolute |
||||
right: $offset |
||||
top: $offset |
||||
|
||||
%overlay |
||||
+overlay |
@ -0,0 +1,87 @@ |
||||
@charset "utf-8"; |
||||
|
||||
$family-sans-serif: "Open Sans", sans-serif; |
||||
$family-code: "Fira Code", monospace; |
||||
|
||||
$body-color: white; |
||||
$strong-color: white; |
||||
|
||||
$link: #feda5a; |
||||
$link-hover: white; |
||||
|
||||
$input-icon-color: #1d1e22; |
||||
$input-icon-active-color: black; |
||||
|
||||
$section-padding-large: 10rem 1.5rem; |
||||
|
||||
$navbar-background-color: #feda6a; |
||||
$navbar-item-color: black; |
||||
$navbar-item-hover-color: white; |
||||
$navbar-item-hover-background-color: #feda6a; |
||||
$navbar-burger-color: black; |
||||
|
||||
$footer-background-color: #393f4d; |
||||
$footer-color: true; |
||||
|
||||
$card-header-color: white; |
||||
|
||||
$table-head-background-color: #feda6a; |
||||
$table-body-background-color: white; |
||||
|
||||
$box-background-color: #feda6a; |
||||
$box-padding: 0.25rem; |
||||
|
||||
$media-border-color: #feda6a; |
||||
|
||||
$menu-item-color: white; |
||||
$menu-item-hover-color: black; |
||||
$menu-item-hover-background-color: #feda6a; |
||||
$menu-label-font-size: 1em; |
||||
|
||||
@import "bulma/utilities/_all"; |
||||
@import "bulma/base/_all"; |
||||
@import "bulma/elements/_all"; |
||||
@import "bulma/form/_all"; |
||||
@import "bulma/components/_all"; |
||||
@import "bulma/grid/_all"; |
||||
@import "bulma/helpers/_all"; |
||||
@import "bulma/layout/_all"; |
||||
|
||||
/* open-sans-regular - latin */ |
||||
@font-face { |
||||
font-family: 'Open Sans'; |
||||
font-style: normal; |
||||
font-weight: 400; |
||||
font-display: swap; |
||||
src: local('Open Sans Regular'), local('OpenSans-Regular'), |
||||
url('/static/css/open-sans-v18-latin-regular.woff2') format('woff2'); /* Chrome 26+, Opera 23+, Firefox 39+ */ |
||||
} |
||||
|
||||
canvas{ |
||||
width: 100%; |
||||
height: auto; |
||||
} |
||||
|
||||
.bg-yayellow{ |
||||
background-color: #feda6a; |
||||
} |
||||
|
||||
.bg-deepmatte{ |
||||
background-color: #393f4d; |
||||
} |
||||
|
||||
.bg-darkslate{ |
||||
background-color: #1d1e22; |
||||
} |
||||
|
||||
.brdr-yayellow{ |
||||
border: 2px solid #feda6a; |
||||
} |
||||
|
||||
.bg-compliment{ |
||||
background-color: #384667; |
||||
} |
||||
|
||||
.fg-yellow{ |
||||
color: #feda6a; |
||||
} |
@ -0,0 +1 @@ |
||||
sass labertasche.scss ../labertasche.css --color --watch --no-source-map |
@ -0,0 +1,86 @@ |
||||
|
||||
@mixin triangle ($size, $color, $direction) { |
||||
height: 0; |
||||
width: 0; |
||||
|
||||
$width: nth($size, 1); |
||||
$height: nth($size, length($size)); |
||||
|
||||
$foreground-color: nth($color, 1); |
||||
$background-color: transparent !default; |
||||
@if (length($color) == 2) { |
||||
$background-color: nth($color, 2); |
||||
} |
||||
|
||||
@if ($direction == up) or ($direction == down) or ($direction == right) or ($direction == left) { |
||||
|
||||
$width: $width / 2; |
||||
|
||||
@if $direction == up { |
||||
border-left: $width solid $background-color; |
||||
border-right: $width solid $background-color; |
||||
border-bottom: $height solid $foreground-color; |
||||
|
||||
} @else if $direction == right { |
||||
border-top: $width solid $background-color; |
||||
border-bottom: $width solid $background-color; |
||||
border-left: $height solid $foreground-color; |
||||
|
||||
} @else if $direction == down { |
||||
border-left: $width solid $background-color; |
||||
border-right: $width solid $background-color; |
||||
border-top: $height solid $foreground-color; |
||||
|
||||
} @else if $direction == left { |
||||
border-top: $width solid $background-color; |
||||
border-bottom: $width solid $background-color; |
||||
border-right: $height solid $foreground-color; |
||||
} |
||||
} |
||||
|
||||
@else if ($direction == up-right) or ($direction == up-left) { |
||||
border-top: $height solid $foreground-color; |
||||
|
||||
@if $direction == up-right { |
||||
border-left: $width solid $background-color; |
||||
|
||||
} @else if $direction == up-left { |
||||
border-right: $width solid $background-color; |
||||
} |
||||
} |
||||
|
||||
@else if ($direction == down-right) or ($direction == down-left) { |
||||
border-bottom: $height solid $foreground-color; |
||||
|
||||
@if $direction == down-right { |
||||
border-left: $width solid $background-color; |
||||
|
||||
} @else if $direction == down-left { |
||||
border-right: $width solid $background-color; |
||||
} |
||||
} |
||||
|
||||
@else if ($direction == inset-up) { |
||||
border-width: $height $width; |
||||
border-style: solid; |
||||
border-color: $background-color $background-color $foreground-color; |
||||
} |
||||
|
||||
@else if ($direction == inset-down) { |
||||
border-width: $height $width; |
||||
border-style: solid; |
||||
border-color: $foreground-color $background-color $background-color; |
||||
} |
||||
|
||||
@else if ($direction == inset-right) { |
||||
border-width: $width $height; |
||||
border-style: solid; |
||||
border-color: $background-color $background-color $background-color $foreground-color; |
||||
} |
||||
|
||||
@else if ($direction == inset-left) { |
||||
border-width: $width $height; |
||||
border-style: solid; |
||||
border-color: $background-color $foreground-color $background-color $background-color; |
||||
} |
||||
} |
File diff suppressed because one or more lines are too long
@ -0,0 +1,21 @@ |
||||
// # /**********************************************************************************
|
||||
// # * _author : Domeniko Gentner
|
||||
// # * _mail : code@tuxstash.de
|
||||
// # * _repo : https://git.tuxstash.de/gothseidank/labertasche
|
||||
// # * _license : This project is under MIT License
|
||||
// # *********************************************************************************/
|
||||
|
||||
function dashboard_mailsearch(search_txt) |
||||
{ |
||||
let el = document.getElementById('mail-table'); |
||||
let children = el.children; |
||||
for (let i = 0; i < children.length; i++ ) |
||||
{ |
||||
children[i].style.display = "none"; |
||||
let iTxt = children[i].innerText.replace(/(\r\n|\n|\r)/gm, "").trim(); |
||||
|
||||
if ( search_txt.value === iTxt.slice(0, search_txt.value.length)){ |
||||
children[i].style.display = "table-row"; |
||||
} |
||||
} |
||||
} |
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in new issue