pixx
v0.0.32
Published
Create web friendly images and code snippets
Downloads
1,264
Maintainers
Readme
pixx
What and Why
- Responsive images can be complex and error prone. This module tries to simplify the image creation and html code to match.
- Using the sharp image library, quickly create responsive images, and the HTML code to match.
- This package only runs in a NodeJS environment.
- Pixx does not increase image size. Start with the largest input image.
- Pixx is designed to use in project development.
- Sharp error on Windows: Could not load the "sharp" module using the win32-x64 runtime.
- Solution:
npm install --include=optional sharp
- Solution:
Simple Start
// download
npm i -D pixx;
// commonjs
const { pixx } = require('pixx');
pixx('compass.jpg').then((HTML) => {});
// esm -package.json "type": "module",
import { pixx } from 'pixx';
const HTML = await pixx('compass.jpg'); // size is 2560w x 1920h.
// returns
<picture>
<source
type="image/avif"
sizes="100vw"
srcset="
pixx_images/compass/compass-400w300h.avif 400w,
pixx_images/compass/compass-800w600h.avif 800w,
pixx_images/compass/compass-1200w900h.avif 1200w,
pixx_images/compass/compass-1600w1200h.avif 1600w,
pixx_images/compass/compass-2000w1500h.avif 2000w,
pixx_images/compass/compass-2400w1800h.avif 2400w,
pixx_images/compass/compass-2560w1920h.avif 2560w
"
/>
<source
type="image/webp"
sizes="100vw"
srcset="
pixx_images/compass/compass-400w300h.webp 400w,
pixx_images/compass/compass-800w600h.webp 800w,
pixx_images/compass/compass-1200w900h.webp 1200w,
pixx_images/compass/compass-1600w1200h.webp 1600w,
pixx_images/compass/compass-2000w1500h.webp 2000w,
pixx_images/compass/compass-2400w1800h.webp 2400w,
pixx_images/compass/compass-2560w1920h.webp 2560w
"
/>
<source
type="image/jpg"
sizes="100vw"
srcset="
pixx_images/compass/compass-400w300h.jpg 400w,
pixx_images/compass/compass-800w600h.jpg 800w,
pixx_images/compass/compass-1200w900h.jpg 1200w,
pixx_images/compass/compass-1600w1200h.jpg 1600w,
pixx_images/compass/compass-2000w1500h.jpg 2000w,
pixx_images/compass/compass-2400w1800h.jpg 2400w,
pixx_images/compass/compass-2560w1920h.jpg 2560w
"
/>
<img
src="pixx_images/compass/compass-2560w1920h.jpg"
alt="image"
width="2560"
height="1920"
loading="eager"
decoding="async"
/>
</picture>;
Understanding Responsive Images: Resolutions Switching, Multiple Types, and Art Direction
- All 'responsive image methods' must have
<meta name="viewport" content="width=device-width">
added to the head section of html, for mobile browsers to use the actual device viewport in decision making. - Responsive Image Advantages
- When mobile or desktop browsers download and parse the HTML, the
sizes
,srcset
andmedia
attribute give clues to the browser about the best images to download. - Using these attributes, the browser decides the best image to download based on its viewport size and pixel density.
- srcset: inform the browser of the available image widths.
- sizes: inform the browser about how much of the viewport the image will fill.
- media: completely different images can be offered depending on matching media condition.
- When mobile or desktop browsers download and parse the HTML, the
Responsive Images
- Three main ways to use responsive images.
- Single image in multiple sizes. (e.g. img-100.jpg, img-200.jpg, img-300.jpg).
- Single image in multiple sizes and types. (e.g. img.avif, img.webp, img.jpg).
- Multiple different images the browser will choose depending on viewport width.
- (e.g. img-full.jpg, img-crop.jpg).
Resolution Switching
- Uses the
img
element with thesrcset
andsizes
attributes. - Single image type. Browsers can choose what image size to download based on the device viewport.
- Fallback is the img
src
attribute. - Pros
- The least complex. Default sizes attribute is
100vw
. - Can offer multiple image size options.
- The least complex. Default sizes attribute is
- Cons
- Only single image type can be used at a time.
// Single image type, multiple sizes.
// e.g. Device viewport has a width of 700px.
// The 'media condition' tells browser image will take 350px (50vw) of viewport.
// If viewport pixel density is 2x. Browser will choose >= 700px image. (compass-800w600h.webp)
await pixx('./compass.jpg', {
picTypes: ['webp'],
sizes: ['(max-width: 450px) 75vw', '(max-width: 800px) 50vw', '25vw'],
});
// returns
<img
srcset="
pixx_images/compass/compass-400w300h.webp 400w,
pixx_images/compass/compass-800w600h.webp 800w,
pixx_images/compass/compass-1200w900h.webp 1200w,
pixx_images/compass/compass-1600w1200h.webp 1600w,
pixx_images/compass/compass-2000w1500h.webp 2000w,
pixx_images/compass/compass-2400w1800h.webp 2400w,
pixx_images/compass/compass-2560w1920h.webp 2560w
"
sizes="(max-width: 450px) 75vw, (max-width: 800px) 50vw, 25vw"
src="pixx_images/compass/compass-2560w1920h.jpg"
alt="image"
width="2560"
height="1920"
loading="eager"
decoding="async"
/>;
Multiple Types
- Uses the
picture
andsource
element with thesrcset
andsizes
attributes. - Same advantages as Resolution Switching, with the added benefit of multiple formats.
- Fallback is
img
element. - Pros
- Use new and highly optimized image formats, with fallback formats for browsers that don't support them.
- Cons
- Code can be complex.
- Order matters. Browser takes the first truthy value.
let pending = true;
await pixx('./src/compass.jpg', {
title: 'Antique compass',
alt: 'Image of an old compass',
withBlur: true,
classes: ['my-special-class', 'border-blue-200', { 'border-red-200': pending }],
});
// console.log blur image path and blurDataURL.
compass.jpg: 'pixx_images/compass/compass-placeholder-10w8h.jpg'
compass.jpg blurDataURL: 'data:image/jpg;base64,/9j/2wBDAAYEBQYFBAYGBQYHBw...'
// returns
<picture>
<source
type="image/avif"
sizes="100vw"
srcset="
pixx_images/compass/compass-400w300h.avif 400w,
pixx_images/compass/compass-800w600h.avif 800w,
pixx_images/compass/compass-1200w900h.avif 1200w,
pixx_images/compass/compass-1600w1200h.avif 1600w,
pixx_images/compass/compass-2000w1500h.avif 2000w,
pixx_images/compass/compass-2400w1800h.avif 2400w,
pixx_images/compass/compass-2560w1920h.avif 2560w
"
/>
<source
type="image/webp"
sizes="100vw"
srcset="
pixx_images/compass/compass-400w300h.webp 400w,
pixx_images/compass/compass-800w600h.webp 800w,
pixx_images/compass/compass-1200w900h.webp 1200w,
pixx_images/compass/compass-1600w1200h.webp 1600w,
pixx_images/compass/compass-2000w1500h.webp 2000w,
pixx_images/compass/compass-2400w1800h.webp 2400w,
pixx_images/compass/compass-2560w1920h.webp 2560w
"
/>
<source
type="image/jpg"
sizes="100vw"
srcset="
pixx_images/compass/compass-400w300h.jpg 400w,
pixx_images/compass/compass-800w600h.jpg 800w,
pixx_images/compass/compass-1200w900h.jpg 1200w,
pixx_images/compass/compass-1600w1200h.jpg 1600w,
pixx_images/compass/compass-2000w1500h.jpg 2000w,
pixx_images/compass/compass-2400w1800h.jpg 2400w,
pixx_images/compass/compass-2560w1920h.jpg 2560w
"
/>
<img
className="my-special-class border-red-200"
src="pixx_images/compass/compass-2560w1920h.jpg"
alt="Image of an old compass"
width="2560"
height="1920"
title="Antique compass"
loading="eager"
decoding="async"
/>
</picture>
Art Direction
- Uses the
picture
andsource
element with thesrcset
,sizes
andmedia
attributes. - Switch different image formats based on first truthy media condition.
- Fallback is
img
element. - Pros
- Switch image based viewport size.
- Cons
- Code can be complex.
- Order matters. Browser takes the first truthy value.
// Art Direction -multiple images: Compass.jpg 2560x1920, Happy face.jpg 720x360
await pixx(['./src/compass.jpg', './src/happy face.jpg'], {
clean: true,
omit: { remove: 'pixx_images', add: './my-special-folder' },
media: ['(min-width: 401px) compass.jpg', '(max-width: 400px) happy face.jpg'],
sizes: ['(min-width: 401px) 50vw', '(max-width: 400px) 100vw', '100vw'],
styles: ['color: blue', 'border-color: red'], // html
});
// returns
<picture>
<source
type="image/avif"
media="(min-width: 401px)"
sizes="(min-width: 401px) 50vw, (max-width: 400px) 100vw, 100vw"
srcset="
./my-special-folder/compass/compass-400w300h.avif 400w,
./my-special-folder/compass/compass-800w600h.avif 800w,
./my-special-folder/compass/compass-1200w900h.avif 1200w,
./my-special-folder/compass/compass-1600w1200h.avif 1600w,
./my-special-folder/compass/compass-2000w1500h.avif 2000w,
./my-special-folder/compass/compass-2400w1800h.avif 2400w,
./my-special-folder/compass/compass-2560w1920h.avif 2560w
"
/>
<source
type="image/webp"
media="(min-width: 401px)"
sizes="(min-width: 401px) 50vw, (max-width: 400px) 100vw, 100vw"
srcset="
./my-special-folder/compass/compass-400w300h.webp 400w,
./my-special-folder/compass/compass-800w600h.webp 800w,
./my-special-folder/compass/compass-1200w900h.webp 1200w,
./my-special-folder/compass/compass-1600w1200h.webp 1600w,
./my-special-folder/compass/compass-2000w1500h.webp 2000w,
./my-special-folder/compass/compass-2400w1800h.webp 2400w,
./my-special-folder/compass/compass-2560w1920h.webp 2560w
"
/>
<source
type="image/jpg"
media="(min-width: 401px)"
sizes="(min-width: 401px) 50vw, (max-width: 400px) 100vw, 100vw"
srcset="
./my-special-folder/compass/compass-400w300h.jpg 400w,
./my-special-folder/compass/compass-800w600h.jpg 800w,
./my-special-folder/compass/compass-1200w900h.jpg 1200w,
./my-special-folder/compass/compass-1600w1200h.jpg 1600w,
./my-special-folder/compass/compass-2000w1500h.jpg 2000w,
./my-special-folder/compass/compass-2400w1800h.jpg 2400w,
./my-special-folder/compass/compass-2560w1920h.jpg 2560w
"
/>
<source
type="image/avif"
media="(max-width: 400px)"
sizes="(min-width: 401px) 50vw, (max-width: 400px) 100vw, 100vw"
srcset="
./my-special-folder/happy_face/happy_face-600w300h.avif 600w,
./my-special-folder/happy_face/happy_face-720w360h.avif 720w
"
/>
<source
type="image/webp"
media="(max-width: 400px)"
sizes="(min-width: 401px) 50vw, (max-width: 400px) 100vw, 100vw"
srcset="
./my-special-folder/happy_face/happy_face-600w300h.webp 600w,
./my-special-folder/happy_face/happy_face-720w360h.webp 720w
"
/>
<source
type="image/jpg"
media="(max-width: 400px)"
sizes="(min-width: 401px) 50vw, (max-width: 400px) 100vw, 100vw"
srcset="
./my-special-folder/happy_face/happy_face-600w300h.jpg 600w,
./my-special-folder/happy_face/happy_face-720w360h.jpg 720w
"
/>
<img
style="color: blue; border-color: red"
src="./my-special-folder/happy_face/happy_face-720w360h.jpg"
alt="image"
width="720"
height="360"
loading="eager"
decoding="auto"
/>
</picture>;
React
- By default, html element is returned as a string. You can return a 'React' component by setting the option
returnReact: true
.
Pixx Options
- alt: string. default
image
. The imgalt
attribute. - blurSize: number. default
10
. Number of pixels wide the placeholder image is resized to.- Bigger blurSize, bigger base64DataURL.
- classes: string[]. Array of class names. Tailwindcss can be used, and optional object syntax.
- e.g.
['my-special-class', 'border-blue-200', { 'border-red-200': pending }]
.
- e.g.
- clean: boolean. Default
false
. Delete image folder and create new. - decoding: enum('auto', 'async', 'sync'). default
auto
. Image download priority. - fallbackWidth: number . Default
image width
. Custom fallback image width in pixels.- Older browsers fallback to this image. Image will always be type
jpg
. - fallbackWidth must be <= image width. Image size is not increased.
- (e.g.
1500
. The fallback img src will be an image 1500px wide with height same aspect ratio as original).
- Older browsers fallback to this image. Image will always be type
- heights: number[]. Array of numbers representing height in pixels.
- heights numbers must be <= image size. Image size is not increased.
- widths have priority over heights. Both have priority over defaultSizes.
- (e.g.
[300, 500, 650, 900, 1200]
).
- incrementSize: number. Default
300
. Customize increment size creation.- The increment pixel size
defaultSizes
uses to create images. - Only valid if
widths
orheights
are empty. - (e.g. Find the smaller image side (width or height), then create img every
300px
until image size is reached).
- The increment pixel size
- linuxPaths: boolean. Default
true
. Development on Windows, convert image paths to linux style. - loading: enum('eager', 'lazy'). Default
eager
. Image loading priority. - log: boolean. Default
false
. Output build details to console.log.- Includes state and hidden image details: EXIF, XMP, ICC, and GPS metadata.
- media: string[]. Array of media conditions and image names.
- Tells browser what image to display based on viewport size.
- This is solely used for Art Direction.
- (e.g.
['(max-width: 400px) img1-crop.jpg', '(min-width: 401px) img1.jpg']
).
- outDir: string. Default
pic_images
. Custom directory name to create images. - omit: { remove: string, add: string }. Object with
remove
andadd
properties.- Customize any part of the image path on the
img
orpicture
elements. - Does not change the
outDir
. Images will still be created in theoutDir
. - (e.g.
{ remove: 'pixx_images', add: './my-special-path' }
).
- Customize any part of the image path on the
- picTypes: enums('avif', 'gif', 'jpeg', 'jpg', 'png', 'tiff', 'webp'). Array of strings.
- Default
['avif', 'webp', 'jpg']
. - (e.g.
['webp']
. Create only webp image types).
- Default
- preload: boolean. Default
false
. Create the image link tag for HTMLhead
element.- Preloading images can optimize load times for critical images.
- Print the link tag to console.log.
- preloadFetchPriority: enum('auto', 'async', 'sync'). Default
auto
. - returnReact: boolean. Default
false
. Return results as React component or string. - sizes: default string[]
['100vw']
. Array of media conditions and the viewport fill width.- MDN sizes. Informs the browser how much of viewport the image will fill based on the media condition.
- The descriptor can be any CSS media condition.
- The value can be any CSS length except percentage. (e.g. 100rem; 75vw; 500px).
- The last item in the array is the 'default' size if media conditions do not match.
- (e.g.
['((min-width: 50em) and (max-width: 60em)) 500px', '75vw']
).
- styles: string[] | { [key: string]: string }. Array(HTML) or Object(React) of inline styles.
- React:
{ color: "blue", backgroundColor: "red" }
- HTML:
['color: blue', 'background-color: red']
- React:
- title: string. Text to display as tooltip when hover over image.
- widths: number[]. Array of widths to create images.
- widths numbers must be <= image size. Image size is not increased.
- widths have priority over heights. Both have priority over defaultSizes.
- (e.g.
[300, 500, 650, 900, 1200]
).
- withAnimation: boolean. Default
false
. Sharp image library will retain the image animation. - withBlur: boolean. Default
false
. Create placeholder image and base64DataURL.- Print to console.log.
- withClassName: boolean. Default
true
. Image class attribute.- Options:
false = class
|true = className
. - Also changes:
false = srcset
|true = srcSet
.
- Options:
- withMetadata: boolean. Default
false
. Copy original image metadata to new images.
pixxFlow
- Pixx was designed to run in an JSX/TSX environment. To use with HTML files, pixxFlow will read your 'static' files and replace the pixx function with the returned html code. The code is statically run.
- In HTML, place a single pixx function in a script tag.
- run file with:
node file.js
- Caution: pixxFlow uses
eval()
to convert the pixx options 'string' to an 'object'. Only use this function in development.
PixxFlow Options
- include: string[]. Files to include. PixxFlow uses glob to search for files.
- ignore?: string[]. Files to ignore.
- log?: boolean. default
false
. Console.log important results for debugging. - debug?: boolean. default
false
. Console.log everything for debugging. - overwrite?: boolean. default
false
. Create a new starting with pixx, or overwrite the file.
// NEXTJS Example
// -npm i -D pixx
// 1. Create run file: file.js
import { pixx, pixxFlow } from 'pixx';
pixxFlow(pixx, {
log: true,
include: ['src/**/*.tsx', 'src/**/*.jsx'],
ignore: ['node_modules', '**/pixx*'],
overwrite: true,
});
// page.tsx
import { pixx } from 'pixx';
export default function Home() {
return (
<div className="grid grid-rows-[20px_1fr_20px]">
<main className="flex flex-col sm:items-start">
{/* Create pixx function. Run dev server. Stop server to build. */}
{pixx('./images/happy face.jpg', {
returnReact: true,
outDir: 'public',
omit: { remove: 'public/' },
})}
</main>
</div>
);
}
// 3. run with:
node file.js
// Returns -------------------------------------
// import { pixx } from 'pixx';
export default function Home() {
return (
<div className="grid grid-rows-[20px_1fr_20px]">
<main className="flex flex-col sm:items-start">
{/* Create pixx function. Run dev server. Stop server to build. */}
{/* pixx('./images/happy face.jpg', {
returnReact: true,
outDir: 'public',
omit: { remove: 'public/' },
}) */}
<picture>
<source
type="image/avif"
sizes="100vw"
srcSet="happy_face/happy_face-600w300h.avif 600w, happy_face/happy_face-720w360h.avif 720w"
/>
<source
type="image/webp"
sizes="100vw"
srcSet="happy_face/happy_face-600w300h.webp 600w, happy_face/happy_face-720w360h.webp 720w"
/>
<source
type="image/jpg"
sizes="100vw"
srcSet="happy_face/happy_face-600w300h.jpg 600w, happy_face/happy_face-720w360h.jpg 720w"
/>
<img
src="happy_face/happy_face-720w360h.jpg"
alt="image"
width="720"
height="360"
loading="eager"
decoding="auto"
/>
</picture>
</main>
</div>
);
}
<!-- HTML Example -->
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Document</title>
</head>
<body>
<p>Example 1</p>
<script>
pixx('./images/img1.webp');
</script>
<p>Example 2</p>
<script>
pixx(['./images/compass.jpg', './images/happy face.jpg'], {
omit: { remove: 'pixx_images', add: './my-special-folder' },
media: ['(min-width: 401px) compass.jpg', '(max-width: 400px) happy face.jpg'],
sizes: ['(min-width: 401px) 50vw', '(max-width: 400px) 100vw', '100vw'],
styles: ['color: blue', 'border-color: red'],
});
</script>
</body>
</html>
<!-- Returns ----------------------------------------------->
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Document</title>
</head>
<body>
<p>Example 1</p>
<!-- <script>
pixx('./images/img1.webp');
</script> -->
<picture>
<source
type="image/avif"
sizes="100vw"
srcset="
pixx_images/img1/img1-300w346h.avif 300w,
pixx_images/img1/img1-600w691h.avif 600w,
pixx_images/img1/img1-750w864h.avif 750w
"
/>
<source
type="image/webp"
sizes="100vw"
srcset="
pixx_images/img1/img1-300w346h.webp 300w,
pixx_images/img1/img1-600w691h.webp 600w,
pixx_images/img1/img1-750w864h.webp 750w
"
/>
<source
type="image/jpg"
sizes="100vw"
srcset="
pixx_images/img1/img1-300w346h.jpg 300w,
pixx_images/img1/img1-600w691h.jpg 600w,
pixx_images/img1/img1-750w864h.jpg 750w
"
/>
<img
src="pixx_images/img1/img1-750w864h.jpg"
alt="image"
width="750"
height="864"
loading="eager"
decoding="auto"
/>
</picture>
<p>Example 2</p>
<!-- <script>
pixx(['./images/compass.jpg', './images/happy face.jpg'], {
omit: { remove: 'pixx_images', add: './my-special-folder' },
media: ['(min-width: 401px) compass.jpg', '(max-width: 400px) happy face.jpg'],
sizes: ['(min-width: 401px) 50vw', '(max-width: 400px) 100vw', '100vw'],
styles: ['color: blue', 'border-color: red'],
});
</script> -->
<picture>
<source
type="image/avif"
media="(min-width: 401px)"
sizes="(min-width: 401px) 50vw, (max-width: 400px) 100vw, 100vw"
srcset="
./my-special-folder/compass/compass-400w300h.avif 400w,
./my-special-folder/compass/compass-800w600h.avif 800w,
./my-special-folder/compass/compass-1200w900h.avif 1200w,
./my-special-folder/compass/compass-1600w1200h.avif 1600w,
./my-special-folder/compass/compass-2000w1500h.avif 2000w,
./my-special-folder/compass/compass-2400w1800h.avif 2400w,
./my-special-folder/compass/compass-2560w1920h.avif 2560w
"
/>
<source
type="image/webp"
media="(min-width: 401px)"
sizes="(min-width: 401px) 50vw, (max-width: 400px) 100vw, 100vw"
srcset="
./my-special-folder/compass/compass-400w300h.webp 400w,
./my-special-folder/compass/compass-800w600h.webp 800w,
./my-special-folder/compass/compass-1200w900h.webp 1200w,
./my-special-folder/compass/compass-1600w1200h.webp 1600w,
./my-special-folder/compass/compass-2000w1500h.webp 2000w,
./my-special-folder/compass/compass-2400w1800h.webp 2400w,
./my-special-folder/compass/compass-2560w1920h.webp 2560w
"
/>
<source
type="image/jpg"
media="(min-width: 401px)"
sizes="(min-width: 401px) 50vw, (max-width: 400px) 100vw, 100vw"
srcset="
./my-special-folder/compass/compass-400w300h.jpg 400w,
./my-special-folder/compass/compass-800w600h.jpg 800w,
./my-special-folder/compass/compass-1200w900h.jpg 1200w,
./my-special-folder/compass/compass-1600w1200h.jpg 1600w,
./my-special-folder/compass/compass-2000w1500h.jpg 2000w,
./my-special-folder/compass/compass-2400w1800h.jpg 2400w,
./my-special-folder/compass/compass-2560w1920h.jpg 2560w
"
/>
<source
type="image/avif"
media="(max-width: 400px)"
sizes="(min-width: 401px) 50vw, (max-width: 400px) 100vw, 100vw"
srcset="
./my-special-folder/happy_face/happy_face-600w300h.avif 600w,
./my-special-folder/happy_face/happy_face-720w360h.avif 720w
"
/>
<source
type="image/webp"
media="(max-width: 400px)"
sizes="(min-width: 401px) 50vw, (max-width: 400px) 100vw, 100vw"
srcset="
./my-special-folder/happy_face/happy_face-600w300h.webp 600w,
./my-special-folder/happy_face/happy_face-720w360h.webp 720w
"
/>
<source
type="image/jpg"
media="(max-width: 400px)"
sizes="(min-width: 401px) 50vw, (max-width: 400px) 100vw, 100vw"
srcset="
./my-special-folder/happy_face/happy_face-600w300h.jpg 600w,
./my-special-folder/happy_face/happy_face-720w360h.jpg 720w
"
/>
<img
style="color: blue; border-color: red"
src="./my-special-folder/happy_face/happy_face-720w360h.jpg"
alt="image"
width="720"
height="360"
loading="eager"
decoding="auto"
/>
</picture>
</body>
</html>
License
Published under the Apache-2.0 license. © Bryon Smith 2024.