# Vanilla Plus JS
[https://vanillaplusjs.com/](https://vanillaplusjs.com/)
This is a framework for building web frontends which are as faithful to vanilla
html/css/js as possible without forcing the developer to sacrifice significant
performance enhancements or to repeat themselves in an error-prone way. The hope
would be that as more features are implemented into the browser, this framework
could slowly be removed until it is no longer necessary.
This framework is compatible with the following content security policy:
`default-src 'self'; object-src 'none';`
This generates a static folder which can be served using any static file server,
but example configurations will only be given for Nginx.
## Folder Structure
A vanillaplusjs project is assumed to be structured as follows:
```
src/
public/
(all your html/js/css files can go here or in subfolders)
img/
(most images go here, see Images)
assets/
(particularly large non-image files go here)
js/
(all your js files go here or in subfolders)
partials/
(html templates go here)
out/
(overwritten by vanillaplusjs)
www/
(this is the folder that is served that you can copy in your CI/CD)
artifacts/
(overwritten by vanillaplusjs)
vanillaplusjs.json (configuration)
```
You should exclude `out/` from your repository via gitignore. It's recommended
to include `artifacts/` in your repository, as it may take a large amount of
memory to produce (e.g., cropping images). You will need to use git-lfs for this.
The recommended `.gitattributes` are
```txt
*.png filter=lfs diff=lfs merge=lfs -text
*.jpg filter=lfs diff=lfs merge=lfs -text
*.jpeg filter=lfs diff=lfs merge=lfs -text
*.webp filter=lfs diff=lfs merge=lfs -text
src/public/assets/* filter=lfs diff=lfs merge=lfs -text
```
## Running
The general concept for setup, once you have a virtual environment, is
```bash
vanillaplusjs init
```
Then to build and run, automatically watching for changes, it's
```bash
vanillaplusjs dev --port 8888 --watch
```
Or to just build it's
```bash
vanillaplusjs build
```
Or to just run (not suitable at all for production) it's
```bash
vanillaplusjs run --port 8888
```
Note that `dev` will build using `vanillaplusjs build --dev` which
may behave very slightly differently than `vanillaplusjs build`;
in particular, see the Constants section.
## Features
### Cache-busting
Projects all need to update themselves occassionally. When they are updated,
the point is that clients receive the updated versions relatively quickly. However,
we also don't want to force each client to download the entire js tree on every
page load - that would dramatically increase server load and bandwidth and reduce
page load speed on the client.
This project will recursively calculate stable hashes of all imported or preloaded
javascript and css files and append them to all import URLs. So for example,
```html
<html>
<head>
<link rel="stylesheet" href="css/style.css">
</head>
<body>Test</body>
</html>
```
will be replaced with something like
```html
<html>
<head>
<link rel="stylesheet" href="css/style.css?v=7e2541f06a8d1f1e2806a3634186917922a219a1b54b76cf04da636fbb7faf87&pv=1">
</head>
<body>Test</body>
</html>
```
This applies to link tags, script tags, and imports within javascript modules. You
should configure your reverse proxy to add cache control headers when there is an
exact match of path, the `v` query parameter, and `pv` query parameter on js/css
files. For example, in nginx
```conf
# ... omitting standard boilerplate ...
location ~* \.(js|css) {
if ($args ~ "^v=[^&]+(&pv=[^&]+)?$") {
add_header Cache-Control "public, max-age=31536000";
}
}
```
### Image preprocessing
A very common task on the web is serving images. When serving images they should
be transcoded to common web formats and compressed as much as possible. This
reduces server load and bandwidth, while reducing page size. The effect can be
extremely dramatic.
Without any tooling this is very tedious and error prone. This project uses
Pillow to perform the image preprocessing, and stores the results in the
`artifacts/` folder. The image preprocessing will take advantage of multiple
cores, but by nature can still be slow. Hence we ensure that the artifacts
folder is byte-for-byte reproducible, thus it can be easily included in your
repository, and then just downloaded by your CI/CD pipeline rather than
regenerated from scratch.
The image processor is deterministic, but it does need to sample different
compression levels to find the best one for a given image. The trade-off
for compression vs accuracy is configurable in `vanillaplusjs.json`.
Our image processing will also handling cropping an image using a `cover-fit`
algorithm. In the most basic case, to render a 375x370 image, it would look
like the following:
```html
<html>
<head>
<title>Image Test</title>
</head>
<body>
<!--[IMAGE: /img/hero/mobile.jpg 375 370]-->
</body>
</html>
```
Which will generate something like the following:
```html
<html>
<head>
<title>Image Test</title>
</head>
<body>
<picture><source srcset="/img/hero/mobile/375x370-100.webp 375w, /img/hero/mobile/562x555-85.webp 562w, /img/hero/mobile/750x740-85.webp 750w, /img/hero/mobile/937x925-75.webp 937w, /img/hero/mobile/1125x1110-75.webp 1125w, /img/hero/mobile/1312x1295-75.webp 1312w, /img/hero/mobile/1500x1480-75.webp 1500w, /img/hero/mobile/1687x1665-75.webp 1687w, /img/hero/mobile/1875x1850-75.webp 1875w" type="image/webp"><img width="375" height="370" loading="lazy" src="/img/hero/mobile/375x370-100.jpeg" srcset="/img/hero/mobile/375x370-100.jpeg 375w, /img/hero/mobile/562x555-85.jpeg 562w, /img/hero/mobile/750x740-85.jpeg 750w, /img/hero/mobile/937x925-85.jpeg 937w, /img/hero/mobile/1125x1110-85.jpeg 1125w, /img/hero/mobile/1312x1295-85.jpeg 1312w, /img/hero/mobile/1500x1480-85.jpeg 1500w, /img/hero/mobile/1687x1665-85.jpeg 1687w, /img/hero/mobile/1875x1850-85.jpeg 1875w"></picture>
</body>
```
Notice how it includes both webp and jpeg files and will have the browser select
the appropriate resolution based on the screen width.
You can also control the crop by specifying a minimum crop for any of the sides.
If the source image is 5000x5000, the following will ensure we only use the
bottom 3600 pixels before resizing in the final image:
```html
<html>
<head>
<title>Image Test</title>
</head>
<body>
<!--[IMAGE: /img/hero/mobile.jpg 375 370 cover {"pre_top_crop": 1400}]-->
</body>
</html>
```
This is typically used as a last resort when the image crop is important for the
legibility of the page, and so you need to use many different crops at different
breakpoints. Typically this comes up for full bleed images with text on them,
where the text has to go over a particular part of the image to have enough
contrast.
#### Static images in javascript
It is sometimes helpful to be able to control images in javascript, but still
take advantage of the preprocessing described above. For this purpose we will
handle the files with the extension `.images.json` as intending to produce a
file called `.images.js`. The JSON file should be in the following format:
```json
{
"img1": {
"path": "/img/admin/img1.jpg",
"width": 100,
"height": 100,
"crop_style": "cover",
"crop_arguments": {},
"lazy": true
}
}
```
This will produce a placeholder file adjacent to it with the extension
`.images.js`:
```js
/**
* The output for this file is generated via the json file by the same name -
* this file is just for type hints.
*/
/**
* Maps from the image name to the image metadata. For each image,
* contains the settings used to produce that image from the source
* image as well as the outputs produced.
*
* @type {Object.<string, {target: {settings: {width: number, height: number, crop: 'cover', crop_settings: {pre_top_crop: number, pre_left_crop: number, pre_bottom_crop: number, pre_right_crop: number, crop_left_percentage: number, crop_top_percentage: number}}, outputs: Object.<string, Array.<{width: number, height: number, url: string, choice: string}>>}}>}
*/
export default {};
```
And will produce a corresponding output file which is functionally
identical to the following:
```js
export default {
"img1": {
"target": {
"outputs": {
"jpeg": [
{
"choice": "100",
"height": 20,
"url": "/img/test/1/20x20.jpeg",
"width": 20
},
{
"choice": "100",
"height": 30,
"url": "/img/test/1/30x30.jpeg",
"width": 30
}
],
"webp": [
{
"choice": "lossless",
"height": 20,
"url": "/img/test/1/20x20.webp",
"width": 20
},
{
"choice": "lossless",
"height": 30,
"url": "/img/test/1/30x30.webp",
"width": 30
}
]
},
"settings": {
"crop": "cover",
"crop_settings": {
"crop_left_percentage": 0.5,
"crop_top_percentage": 0.5,
"pre_bottom_crop": 0,
"pre_left_crop": 0,
"pre_right_crop": 0,
"pre_top_crop": 0
},
"height": 20,
"width": 20
}
}
}
};
```
### Outlining js/css
Vanillaplusjs is expected to be run under the strictest content security policy,
and to facilitate this will automatically outline scripts and stylesheets into
their own separate files which are imported appropriately.
It's recommended that only test scripts or _very_ small script tags utilize
this, but it does avoid CSP issues causing code to work when running locally
that fails when deployed.
### Templates
Some parts of the HTML are quite repetitive. This library does not intend
to completely remove the boilerplate and does prefer clarity over brevity,
however, some amount of DRY is necessary to keep the code readable.
Extremely basic HTML templating is supported with constant variables.
The template file should go in the `partials` subdirectory of `src` folder
and works as follows:
src/public/index.html
```html
<!DOCTYPE html>
<html>
<head>
<!--[TEMPLATE: ["/head/standard.html", {"title": "Try it Now"}]]-->
</head>
<body>
</body>
</html>
```
src/partials/head/standard.html
```html
<title><!--[STACK: ["retrieve", "title"]]--></title>
<meta charset="utf-8">
```
You can also use the stack to define local variables:
```html
<!--[STACK: ["define", "price", "$24.99"]]-->
<p>Get it today! Just <!--[STACK: ["retrieve", "price"]]--></p>
```
### Outlining Images
If images are specified in CSS files via data URIs, they will be outlined
in order to support the desired content security policy. This is particularly
useful when combined with external files, since e.g., bootstrap will inline
SVGs.
This a performance trade-off and can cause some flashing, but this project
prefers the security assurances of the CSP to the performance gains of inlined
scripts and images. Note that the browser could resolve this flashing in the
future by loading svgs referenced in stylesheets in the background after the
page has loaded.
### External files
If you have files that are required but should not be distributed via source
control then you can instruct vanillaplusjs to download them from a CDN prior to
building. For example, if you want to include bootstrap 5.2.0-beta1 in your project, you
would update the `external_files` section of `vanillaplusjs.json` as follows:
```json
{
"external_files": {
"src/public/css/lib/bootstrap.min.css": {
"url": "https://cdn.jsdelivr.net/npm/bootstrap@5.2.0-beta1/dist/css/bootstrap.min.css",
"integrity": "sha384-0evHe/X+R7YkIZDRvuzKMRqM+OrBnVFBL6DOitfPri4tjfHxaWutUpFmBp4vmVor"
},
"src/public/js/lib/bootstrap.min.js": {
"url": "https://cdn.jsdelivr.net/npm/bootstrap@5.2.0-beta1/dist/js/bootstrap.bundle.min.js",
"integrity": "sha384-pprn3073KE6tl6bjs2QrFaJGz5/SUsLqktiwsUTF55Jfv3qYSDhgCecCxMW52nD2"
}
}
}
```
Note that to update dependencies you must do a cold build (`vanillaplusjs build`) as they will not
be updated during a hot build (`vanillaplusjs dev --watch`)
### Canonical URLs
For SEO purposes it's often necessary to set a canonical URL for a page.
These URLs can't be reliably specified via relative URLs. This is a bit
of a pain if you want to be able to change the domain name of the site,
e.g., to facilitate test environments.
Hence the preprocessor makes these easier to work with. For the most
common case where the canonical url should just be the url where the
file would be accessed using a standard static file server, just omit
the href:
```html
<link rel="canonical">
```
an the tag will be replaced with the full url include the file extension for you:
```html
<link href="https://example.com/account.html" rel="canonical">
```
Alternatively, you can specify the relative path:
```html
<link href="/" rel="canonical">
```
becomes
```html
<link href="https://example.com/" rel="canonical">
```
This functions identically for the meta property `og:url`
### CSS Nesting
CSS without nesting can quickly be painful if you want to support the main
browsers. We support a very simplistic nesting system. We assume you have a
`/css/main.css` file which will act as the default source of imports. In that
file you can do:
```css
.unstyle {
appearance: none;
border: none;
box-shadow: none;
/* etc */
}
.button {
/*! PREPROCESSOR: import .unstyle */
/* etc */
}
```
For colors and other variables it's recommended you use CSS custom properties.
### Icons
Icons with a strict CSP can also be somewhat painful to use. We have a specific
CSS preprocessor action for dealing with icons, supposing that you follow a
specific naming convention.
Suppose you have an SVG icon for a navbar toggler, which we will call
`navbar-toggler.svg`, and this is a pretty simplistic svg: it has exactly one
color in it. Now also suppose that you also have defined CSS variables following
the given pattern on the `:root` selector in the `src/public/css/main.css` file:
```css
:root {
--col-primary: #333;
--col-primary-dark: #000;
--col-primary-light: #666;
--icon-size-large: 1.5rem;
--icon-size-medium: 1rem;
--icon-size-small: 0.75rem;
}
```
Lets assume the svg is specified in `primary`, ie., the only color in the file
is `#333` (`--col-primary`).
If the icon is stored at `src/public/img/icons/navbar-toggler.svg`, then in CSS
you may use the following preprocessor command:
```css
/*! PREPROCESSOR: icon navbar-toggler primary all-colors all-sizes */
```
and it will generate the following classes:
- `.icon-navbar-toggler-large-primary`
- `.icon-navbar-toggler-medium-primary`
- `.icon-navbar-toggler-small-primary`
- `.icon-navbar-toggler-large-primary-dark`
- `.icon-navbar-toggler-medium-primary-dark`
- `.icon-navbar-toggler-small-primary-dark`
- `.icon-navbar-toggler-large-primary-light`
- `.icon-navbar-toggler-medium-primary-light`
- `.icon-navbar-toggler-small-primary-light`
- `.icon-btn-navbar-toggler-large`
- `.icon-btn-navbar-toggler-medium`
- `.icon-btn-navbar-toggler-small`
Which can be used as follows:
To just show the icon in html:
```html
<span>Hello, heres the toggler: <span class="icon-navbar-toggler-large-primary"></span></span>
```
Or you can create a button which will have hover and disabled colors:
```html
<button class="icon-btn-navbar-toggler-large" title="Toggle Navigation">
<span class="icon-btn--icon"></span>
</button>
```
You can instead specify the exact colors or sizes you want to generate, which is
particularly useful if you have a lot of colors, and you can suppress the button
class
```css
/*! PREPROCESSOR: icon navbar-toggler primary ["primary-dark"] all-sizes no-btn */
```
would only generate the `primary-dark` icon color, and would do so for all the
sizes specified.
In case you need it, such as to reference the icon in other css, the
large primary-dark file would be at
`/img/icons/navbar-toggler/large/primary-dark.svg`
### Constants
Editable in the `vanillaplusjs.json` file, you can specify a file that acts as the
constants file. For example,
```json
{
"js_constants": {
"relpath": "src/public/js/constants.js",
"shared": {},
"dev": {"API_URL": "http://127.0.0.1:8080"},
"prod": {"API_URL": ""},
}
}
```
Then, if you create the file at the corresponding path, in this case,
`src/public/js/constants.js`, the contents of that file will be ignored and the
outputted file will depend on the build environment. In production mode,
`vanillaplusjs build`, the file at `out/www/js/constants.js` will be
```js
export const API_URL = "";
```
In development mode, `vanillaplusjs build --dev`, it will instead be
```js
export const API_URL = "http://127.0.0.1:8080";
```
This can be used, for example, to create an API wrapper:
```js
import { API_URL } from '/js/constants.js';
export function apiFetch(url, options) {
if (url.startsWith('/')) {
url = API_URL + url;
}
return fetch(url, options);
}
```
Which will allow you to run the backend server on a different port
locally, but in production mode, it will use the same port as the frontend.
### Commenting Non-Functional Lines
Non-functional lines of javascript which are used primarily for type hints,
such as those for describing function overloading, can be commented out by
including a comment which is exclusively `@@type-hints` (plus whitespace).
For example:
```js
export function foo(a, b); // @type-hint
export function foo(...args) {
// ...
}
```
will become
```js
/*export function foo(a, b); /* @@type-hint *\/*/
export function foo(...args) {
/* ...*/
}
```
## Contributing
This package uses `pre-commit` to install git commit hooks. Before
contributing, configure your virtual environment with the development
dependencies and initialize the pre-commit hooks:
```bash
python -m venv venv
"venv/bin/activate"
python -m pip install -U pip
pip install -r requirements.txt
pre-commit install
```
For windows
```bash
python -m venv venv
"venv/Scripts/Activate.bat"
python -m pip install -U pip
pip install -r requirements.txt
```
Then, in Git Bash for Windows,
```bash
"venv/Scripts/pre-commit.exe" install
```
Raw data
{
"_id": null,
"home_page": "https://github.com/tjstretchalot/vanillaplusjs",
"name": "vanillaplusjs",
"maintainer": "",
"docs_url": null,
"requires_python": "",
"maintainer_email": "",
"keywords": "",
"author": "Timothy Moore",
"author_email": "mtimothy984@gmail.com",
"download_url": "https://files.pythonhosted.org/packages/60/57/ab34d8f1bb157aeacfe414045ead7ec6004f3ad6e0a2dcff3039d614be43/vanillaplusjs-1.3.2.tar.gz",
"platform": null,
"description": "# Vanilla Plus JS\n\n[https://vanillaplusjs.com/](https://vanillaplusjs.com/)\n\nThis is a framework for building web frontends which are as faithful to vanilla\nhtml/css/js as possible without forcing the developer to sacrifice significant\nperformance enhancements or to repeat themselves in an error-prone way. The hope\nwould be that as more features are implemented into the browser, this framework\ncould slowly be removed until it is no longer necessary.\n\nThis framework is compatible with the following content security policy:\n`default-src 'self'; object-src 'none';`\n\nThis generates a static folder which can be served using any static file server,\nbut example configurations will only be given for Nginx.\n\n## Folder Structure\n\nA vanillaplusjs project is assumed to be structured as follows:\n\n```\nsrc/\n public/\n (all your html/js/css files can go here or in subfolders)\n img/\n (most images go here, see Images)\n assets/\n (particularly large non-image files go here)\n js/\n (all your js files go here or in subfolders)\n partials/\n (html templates go here)\nout/\n (overwritten by vanillaplusjs)\n www/\n (this is the folder that is served that you can copy in your CI/CD)\nartifacts/\n (overwritten by vanillaplusjs)\nvanillaplusjs.json (configuration)\n```\n\nYou should exclude `out/` from your repository via gitignore. It's recommended\nto include `artifacts/` in your repository, as it may take a large amount of\nmemory to produce (e.g., cropping images). You will need to use git-lfs for this.\nThe recommended `.gitattributes` are\n\n```txt\n*.png filter=lfs diff=lfs merge=lfs -text\n*.jpg filter=lfs diff=lfs merge=lfs -text\n*.jpeg filter=lfs diff=lfs merge=lfs -text\n*.webp filter=lfs diff=lfs merge=lfs -text\nsrc/public/assets/* filter=lfs diff=lfs merge=lfs -text\n```\n\n## Running\n\nThe general concept for setup, once you have a virtual environment, is\n\n```bash\nvanillaplusjs init\n```\n\nThen to build and run, automatically watching for changes, it's\n\n```bash\nvanillaplusjs dev --port 8888 --watch\n```\n\nOr to just build it's\n\n```bash\nvanillaplusjs build\n```\n\nOr to just run (not suitable at all for production) it's\n\n```bash\nvanillaplusjs run --port 8888\n```\n\nNote that `dev` will build using `vanillaplusjs build --dev` which\nmay behave very slightly differently than `vanillaplusjs build`;\nin particular, see the Constants section.\n\n## Features\n\n### Cache-busting\n\nProjects all need to update themselves occassionally. When they are updated,\nthe point is that clients receive the updated versions relatively quickly. However,\nwe also don't want to force each client to download the entire js tree on every\npage load - that would dramatically increase server load and bandwidth and reduce\npage load speed on the client.\n\nThis project will recursively calculate stable hashes of all imported or preloaded\njavascript and css files and append them to all import URLs. So for example,\n\n```html\n<html>\n<head>\n <link rel=\"stylesheet\" href=\"css/style.css\">\n</head>\n<body>Test</body>\n</html>\n```\n\nwill be replaced with something like\n\n```html\n<html>\n<head>\n <link rel=\"stylesheet\" href=\"css/style.css?v=7e2541f06a8d1f1e2806a3634186917922a219a1b54b76cf04da636fbb7faf87&pv=1\">\n</head>\n<body>Test</body>\n</html>\n```\n\nThis applies to link tags, script tags, and imports within javascript modules. You\nshould configure your reverse proxy to add cache control headers when there is an\nexact match of path, the `v` query parameter, and `pv` query parameter on js/css\nfiles. For example, in nginx\n\n```conf\n# ... omitting standard boilerplate ...\nlocation ~* \\.(js|css) {\n if ($args ~ \"^v=[^&]+(&pv=[^&]+)?$\") {\n add_header Cache-Control \"public, max-age=31536000\";\n }\n}\n```\n\n### Image preprocessing\n\nA very common task on the web is serving images. When serving images they should\nbe transcoded to common web formats and compressed as much as possible. This\nreduces server load and bandwidth, while reducing page size. The effect can be\nextremely dramatic.\n\nWithout any tooling this is very tedious and error prone. This project uses\nPillow to perform the image preprocessing, and stores the results in the\n`artifacts/` folder. The image preprocessing will take advantage of multiple\ncores, but by nature can still be slow. Hence we ensure that the artifacts\nfolder is byte-for-byte reproducible, thus it can be easily included in your\nrepository, and then just downloaded by your CI/CD pipeline rather than\nregenerated from scratch.\n\nThe image processor is deterministic, but it does need to sample different\ncompression levels to find the best one for a given image. The trade-off\nfor compression vs accuracy is configurable in `vanillaplusjs.json`.\n\nOur image processing will also handling cropping an image using a `cover-fit`\nalgorithm. In the most basic case, to render a 375x370 image, it would look\nlike the following:\n\n\n```html\n<html>\n<head>\n <title>Image Test</title>\n</head>\n<body>\n <!--[IMAGE: /img/hero/mobile.jpg 375 370]-->\n</body>\n</html>\n```\n\nWhich will generate something like the following:\n\n```html\n<html>\n<head>\n <title>Image Test</title>\n</head>\n<body>\n <picture><source srcset=\"/img/hero/mobile/375x370-100.webp 375w, /img/hero/mobile/562x555-85.webp 562w, /img/hero/mobile/750x740-85.webp 750w, /img/hero/mobile/937x925-75.webp 937w, /img/hero/mobile/1125x1110-75.webp 1125w, /img/hero/mobile/1312x1295-75.webp 1312w, /img/hero/mobile/1500x1480-75.webp 1500w, /img/hero/mobile/1687x1665-75.webp 1687w, /img/hero/mobile/1875x1850-75.webp 1875w\" type=\"image/webp\"><img width=\"375\" height=\"370\" loading=\"lazy\" src=\"/img/hero/mobile/375x370-100.jpeg\" srcset=\"/img/hero/mobile/375x370-100.jpeg 375w, /img/hero/mobile/562x555-85.jpeg 562w, /img/hero/mobile/750x740-85.jpeg 750w, /img/hero/mobile/937x925-85.jpeg 937w, /img/hero/mobile/1125x1110-85.jpeg 1125w, /img/hero/mobile/1312x1295-85.jpeg 1312w, /img/hero/mobile/1500x1480-85.jpeg 1500w, /img/hero/mobile/1687x1665-85.jpeg 1687w, /img/hero/mobile/1875x1850-85.jpeg 1875w\"></picture>\n</body>\n```\n\nNotice how it includes both webp and jpeg files and will have the browser select\nthe appropriate resolution based on the screen width.\n\nYou can also control the crop by specifying a minimum crop for any of the sides.\nIf the source image is 5000x5000, the following will ensure we only use the\nbottom 3600 pixels before resizing in the final image:\n\n```html\n<html>\n<head>\n <title>Image Test</title>\n</head>\n<body>\n <!--[IMAGE: /img/hero/mobile.jpg 375 370 cover {\"pre_top_crop\": 1400}]-->\n</body>\n</html>\n```\n\nThis is typically used as a last resort when the image crop is important for the\nlegibility of the page, and so you need to use many different crops at different\nbreakpoints. Typically this comes up for full bleed images with text on them,\nwhere the text has to go over a particular part of the image to have enough\ncontrast.\n\n#### Static images in javascript\n\nIt is sometimes helpful to be able to control images in javascript, but still\ntake advantage of the preprocessing described above. For this purpose we will\nhandle the files with the extension `.images.json` as intending to produce a\nfile called `.images.js`. The JSON file should be in the following format:\n\n```json\n{\n \"img1\": {\n \"path\": \"/img/admin/img1.jpg\",\n \"width\": 100,\n \"height\": 100,\n \"crop_style\": \"cover\",\n \"crop_arguments\": {},\n \"lazy\": true\n }\n}\n```\n\nThis will produce a placeholder file adjacent to it with the extension\n`.images.js`:\n\n```js\n/**\n * The output for this file is generated via the json file by the same name -\n * this file is just for type hints.\n */\n\n/**\n * Maps from the image name to the image metadata. For each image,\n * contains the settings used to produce that image from the source\n * image as well as the outputs produced.\n *\n * @type {Object.<string, {target: {settings: {width: number, height: number, crop: 'cover', crop_settings: {pre_top_crop: number, pre_left_crop: number, pre_bottom_crop: number, pre_right_crop: number, crop_left_percentage: number, crop_top_percentage: number}}, outputs: Object.<string, Array.<{width: number, height: number, url: string, choice: string}>>}}>}\n */\nexport default {};\n```\n\nAnd will produce a corresponding output file which is functionally\nidentical to the following:\n\n```js\nexport default {\n \"img1\": {\n \"target\": {\n \"outputs\": {\n \"jpeg\": [\n {\n \"choice\": \"100\",\n \"height\": 20,\n \"url\": \"/img/test/1/20x20.jpeg\",\n \"width\": 20\n },\n {\n \"choice\": \"100\",\n \"height\": 30,\n \"url\": \"/img/test/1/30x30.jpeg\",\n \"width\": 30\n }\n ],\n \"webp\": [\n {\n \"choice\": \"lossless\",\n \"height\": 20,\n \"url\": \"/img/test/1/20x20.webp\",\n \"width\": 20\n },\n {\n \"choice\": \"lossless\",\n \"height\": 30,\n \"url\": \"/img/test/1/30x30.webp\",\n \"width\": 30\n }\n ]\n },\n \"settings\": {\n \"crop\": \"cover\",\n \"crop_settings\": {\n \"crop_left_percentage\": 0.5,\n \"crop_top_percentage\": 0.5,\n \"pre_bottom_crop\": 0,\n \"pre_left_crop\": 0,\n \"pre_right_crop\": 0,\n \"pre_top_crop\": 0\n },\n \"height\": 20,\n \"width\": 20\n }\n }\n }\n};\n```\n\n### Outlining js/css\n\nVanillaplusjs is expected to be run under the strictest content security policy,\nand to facilitate this will automatically outline scripts and stylesheets into\ntheir own separate files which are imported appropriately.\n\nIt's recommended that only test scripts or _very_ small script tags utilize\nthis, but it does avoid CSP issues causing code to work when running locally\nthat fails when deployed.\n\n### Templates\n\nSome parts of the HTML are quite repetitive. This library does not intend\nto completely remove the boilerplate and does prefer clarity over brevity,\nhowever, some amount of DRY is necessary to keep the code readable.\nExtremely basic HTML templating is supported with constant variables.\n\nThe template file should go in the `partials` subdirectory of `src` folder\nand works as follows:\n\nsrc/public/index.html\n\n```html\n<!DOCTYPE html>\n<html>\n<head>\n <!--[TEMPLATE: [\"/head/standard.html\", {\"title\": \"Try it Now\"}]]-->\n</head>\n<body>\n</body>\n</html>\n```\n\nsrc/partials/head/standard.html\n\n```html\n<title><!--[STACK: [\"retrieve\", \"title\"]]--></title>\n<meta charset=\"utf-8\">\n```\n\nYou can also use the stack to define local variables:\n\n```html\n<!--[STACK: [\"define\", \"price\", \"$24.99\"]]-->\n<p>Get it today! Just <!--[STACK: [\"retrieve\", \"price\"]]--></p>\n```\n\n### Outlining Images\n\nIf images are specified in CSS files via data URIs, they will be outlined\nin order to support the desired content security policy. This is particularly\nuseful when combined with external files, since e.g., bootstrap will inline\nSVGs.\n\nThis a performance trade-off and can cause some flashing, but this project\nprefers the security assurances of the CSP to the performance gains of inlined\nscripts and images. Note that the browser could resolve this flashing in the\nfuture by loading svgs referenced in stylesheets in the background after the\npage has loaded.\n\n### External files\n\nIf you have files that are required but should not be distributed via source\ncontrol then you can instruct vanillaplusjs to download them from a CDN prior to\nbuilding. For example, if you want to include bootstrap 5.2.0-beta1 in your project, you\nwould update the `external_files` section of `vanillaplusjs.json` as follows:\n\n```json\n{\n \"external_files\": {\n \"src/public/css/lib/bootstrap.min.css\": {\n \"url\": \"https://cdn.jsdelivr.net/npm/bootstrap@5.2.0-beta1/dist/css/bootstrap.min.css\",\n \"integrity\": \"sha384-0evHe/X+R7YkIZDRvuzKMRqM+OrBnVFBL6DOitfPri4tjfHxaWutUpFmBp4vmVor\"\n },\n \"src/public/js/lib/bootstrap.min.js\": {\n \"url\": \"https://cdn.jsdelivr.net/npm/bootstrap@5.2.0-beta1/dist/js/bootstrap.bundle.min.js\",\n \"integrity\": \"sha384-pprn3073KE6tl6bjs2QrFaJGz5/SUsLqktiwsUTF55Jfv3qYSDhgCecCxMW52nD2\"\n }\n }\n}\n```\n\nNote that to update dependencies you must do a cold build (`vanillaplusjs build`) as they will not\nbe updated during a hot build (`vanillaplusjs dev --watch`)\n\n### Canonical URLs\n\nFor SEO purposes it's often necessary to set a canonical URL for a page.\nThese URLs can't be reliably specified via relative URLs. This is a bit\nof a pain if you want to be able to change the domain name of the site,\ne.g., to facilitate test environments.\n\nHence the preprocessor makes these easier to work with. For the most\ncommon case where the canonical url should just be the url where the\nfile would be accessed using a standard static file server, just omit\nthe href:\n\n```html\n<link rel=\"canonical\">\n```\n\nan the tag will be replaced with the full url include the file extension for you:\n\n```html\n<link href=\"https://example.com/account.html\" rel=\"canonical\">\n```\n\nAlternatively, you can specify the relative path:\n\n\n```html\n<link href=\"/\" rel=\"canonical\">\n```\n\nbecomes\n\n```html\n<link href=\"https://example.com/\" rel=\"canonical\">\n```\n\nThis functions identically for the meta property `og:url`\n\n### CSS Nesting\n\nCSS without nesting can quickly be painful if you want to support the main\nbrowsers. We support a very simplistic nesting system. We assume you have a\n`/css/main.css` file which will act as the default source of imports. In that\nfile you can do:\n\n```css\n.unstyle {\n appearance: none;\n border: none;\n box-shadow: none;\n /* etc */\n}\n\n.button {\n /*! PREPROCESSOR: import .unstyle */\n /* etc */\n}\n```\n\nFor colors and other variables it's recommended you use CSS custom properties.\n\n### Icons\n\nIcons with a strict CSP can also be somewhat painful to use. We have a specific\nCSS preprocessor action for dealing with icons, supposing that you follow a\nspecific naming convention.\n\nSuppose you have an SVG icon for a navbar toggler, which we will call\n`navbar-toggler.svg`, and this is a pretty simplistic svg: it has exactly one\ncolor in it. Now also suppose that you also have defined CSS variables following\nthe given pattern on the `:root` selector in the `src/public/css/main.css` file:\n\n```css\n:root {\n --col-primary: #333;\n --col-primary-dark: #000;\n --col-primary-light: #666;\n --icon-size-large: 1.5rem;\n --icon-size-medium: 1rem;\n --icon-size-small: 0.75rem;\n}\n```\n\nLets assume the svg is specified in `primary`, ie., the only color in the file\nis `#333` (`--col-primary`).\n\nIf the icon is stored at `src/public/img/icons/navbar-toggler.svg`, then in CSS\nyou may use the following preprocessor command:\n\n```css\n/*! PREPROCESSOR: icon navbar-toggler primary all-colors all-sizes */\n```\n\nand it will generate the following classes:\n\n- `.icon-navbar-toggler-large-primary`\n- `.icon-navbar-toggler-medium-primary`\n- `.icon-navbar-toggler-small-primary`\n- `.icon-navbar-toggler-large-primary-dark`\n- `.icon-navbar-toggler-medium-primary-dark`\n- `.icon-navbar-toggler-small-primary-dark`\n- `.icon-navbar-toggler-large-primary-light`\n- `.icon-navbar-toggler-medium-primary-light`\n- `.icon-navbar-toggler-small-primary-light`\n- `.icon-btn-navbar-toggler-large`\n- `.icon-btn-navbar-toggler-medium`\n- `.icon-btn-navbar-toggler-small`\n\nWhich can be used as follows:\n\nTo just show the icon in html:\n\n```html\n<span>Hello, heres the toggler: <span class=\"icon-navbar-toggler-large-primary\"></span></span>\n```\n\nOr you can create a button which will have hover and disabled colors:\n\n```html\n<button class=\"icon-btn-navbar-toggler-large\" title=\"Toggle Navigation\">\n <span class=\"icon-btn--icon\"></span>\n</button>\n```\n\nYou can instead specify the exact colors or sizes you want to generate, which is\nparticularly useful if you have a lot of colors, and you can suppress the button\nclass\n\n```css\n/*! PREPROCESSOR: icon navbar-toggler primary [\"primary-dark\"] all-sizes no-btn */\n```\n\nwould only generate the `primary-dark` icon color, and would do so for all the\nsizes specified.\n\nIn case you need it, such as to reference the icon in other css, the\nlarge primary-dark file would be at\n`/img/icons/navbar-toggler/large/primary-dark.svg`\n\n### Constants\n\nEditable in the `vanillaplusjs.json` file, you can specify a file that acts as the\nconstants file. For example,\n\n```json\n{\n \"js_constants\": {\n \"relpath\": \"src/public/js/constants.js\",\n \"shared\": {},\n \"dev\": {\"API_URL\": \"http://127.0.0.1:8080\"},\n \"prod\": {\"API_URL\": \"\"},\n }\n}\n```\n\nThen, if you create the file at the corresponding path, in this case,\n`src/public/js/constants.js`, the contents of that file will be ignored and the\noutputted file will depend on the build environment. In production mode,\n`vanillaplusjs build`, the file at `out/www/js/constants.js` will be\n\n```js\nexport const API_URL = \"\";\n```\n\nIn development mode, `vanillaplusjs build --dev`, it will instead be\n\n```js\nexport const API_URL = \"http://127.0.0.1:8080\";\n```\n\nThis can be used, for example, to create an API wrapper:\n\n```js\nimport { API_URL } from '/js/constants.js';\n\n\nexport function apiFetch(url, options) {\n if (url.startsWith('/')) {\n url = API_URL + url;\n }\n\n return fetch(url, options);\n}\n```\n\nWhich will allow you to run the backend server on a different port\nlocally, but in production mode, it will use the same port as the frontend.\n\n### Commenting Non-Functional Lines\n\nNon-functional lines of javascript which are used primarily for type hints,\nsuch as those for describing function overloading, can be commented out by\nincluding a comment which is exclusively `@@type-hints` (plus whitespace).\nFor example:\n\n```js\nexport function foo(a, b); // @type-hint\nexport function foo(...args) {\n // ...\n}\n```\n\nwill become\n\n```js\n/*export function foo(a, b); /* @@type-hint *\\/*/\nexport function foo(...args) {\n /* ...*/\n}\n```\n## Contributing\n\nThis package uses `pre-commit` to install git commit hooks. Before\ncontributing, configure your virtual environment with the development\ndependencies and initialize the pre-commit hooks:\n\n```bash\npython -m venv venv\n\"venv/bin/activate\"\npython -m pip install -U pip\npip install -r requirements.txt\npre-commit install\n```\n\nFor windows\n\n```bash\npython -m venv venv\n\"venv/Scripts/Activate.bat\"\npython -m pip install -U pip\npip install -r requirements.txt\n```\n\nThen, in Git Bash for Windows,\n\n```bash\n\"venv/Scripts/pre-commit.exe\" install\n```\n",
"bugtrack_url": null,
"license": "",
"summary": "A lightweight html/css/js framework",
"version": "1.3.2",
"split_keywords": [],
"urls": [
{
"comment_text": "",
"digests": {
"md5": "5e5f09f7b7562f0d368724bd34565a2c",
"sha256": "82206ac527fdf52491f448c101bf2a07881c4d6b694fef5d417947480cecdb0c"
},
"downloads": -1,
"filename": "vanillaplusjs-1.3.2-py3-none-any.whl",
"has_sig": false,
"md5_digest": "5e5f09f7b7562f0d368724bd34565a2c",
"packagetype": "bdist_wheel",
"python_version": "py3",
"requires_python": null,
"size": 139652,
"upload_time": "2022-12-14T20:37:09",
"upload_time_iso_8601": "2022-12-14T20:37:09.005327Z",
"url": "https://files.pythonhosted.org/packages/b8/95/c57ae18c81a29d5e7e83e9663b1e6a99b99355ec273f0a7be84a047187c2/vanillaplusjs-1.3.2-py3-none-any.whl",
"yanked": false,
"yanked_reason": null
},
{
"comment_text": "",
"digests": {
"md5": "612de2ede2a20204ee80b6226ebcd20a",
"sha256": "985f8efa85945275b8eb061b38e053a5036a651e4c683e74399bbb8e0a90ec37"
},
"downloads": -1,
"filename": "vanillaplusjs-1.3.2.tar.gz",
"has_sig": false,
"md5_digest": "612de2ede2a20204ee80b6226ebcd20a",
"packagetype": "sdist",
"python_version": "source",
"requires_python": null,
"size": 116418,
"upload_time": "2022-12-14T20:37:10",
"upload_time_iso_8601": "2022-12-14T20:37:10.816062Z",
"url": "https://files.pythonhosted.org/packages/60/57/ab34d8f1bb157aeacfe414045ead7ec6004f3ad6e0a2dcff3039d614be43/vanillaplusjs-1.3.2.tar.gz",
"yanked": false,
"yanked_reason": null
}
],
"upload_time": "2022-12-14 20:37:10",
"github": true,
"gitlab": false,
"bitbucket": false,
"github_user": "tjstretchalot",
"github_project": "vanillaplusjs",
"travis_ci": false,
"coveralls": false,
"github_actions": true,
"requirements": [],
"lcname": "vanillaplusjs"
}