diff --git a/.changeset/polite-pears-hope.md b/.changeset/polite-pears-hope.md
new file mode 100644
index 000000000000..6facd8e89cb6
--- /dev/null
+++ b/.changeset/polite-pears-hope.md
@@ -0,0 +1,5 @@
+---
+'@astrojs/image': minor
+---
+
+feat: throw if alt text is missing
diff --git a/packages/integrations/image/README.md b/packages/integrations/image/README.md
index 7557c4ecd42e..a1ffa25ae781 100644
--- a/packages/integrations/image/README.md
+++ b/packages/integrations/image/README.md
@@ -18,7 +18,7 @@ This **[Astro integration][astro-integration]** makes it easy to optimize images
Images play a big role in overall site performance and usability. Serving properly sized images makes all the difference but is often tricky to automate.
-This integration provides `` and `` components as well as a basic image transformer powered by [sharp](https://sharp.pixelplumbing.com/), with full support for static sites and server-side rendering. The built-in `sharp` transformer is also replacable, opening the door for future integrations that work with your favorite hosted image service.
+This integration provides `` and `` components as well as a basic image transformer powered by [sharp](https://sharp.pixelplumbing.com/), with full support for static sites and server-side rendering. The built-in `sharp` transformer is also replaceable, opening the door for future integrations that work with your favorite hosted image service.
## Installation
@@ -90,6 +90,10 @@ import { Image, Picture } from '@astrojs/image/components';
The included `sharp` transformer supports resizing images and encoding them to different image formats. Third-party image services will be able to add support for custom transformations as well (ex: `blur`, `filter`, `rotate`, etc).
+Astro’s and components require the alt attribute which provides descriptive text for images. A warning will be logged if "alt" text is missing, and a future release of the integration will throw an error if no alt text is provided.
+
+If the image is merely decorative (i.e. doesn’t contribute to the understanding of the page), set alt="" so that the image is properly understood and ignored by screen readers.
+
### ``
The built-in `` component is used to create an optimized `` for both remote images hosted on other domains as well as local images imported from your project's `src` directory.
@@ -108,10 +112,22 @@ Source for the original image file.
For images located in your project's `src`: use the file path relative to the `src` directory. (e.g. `src="../assets/source-pic.png"`)
- For images located in your `public` directory: use the URL path relative to the `public` directory. (e.g. `src="/images/public-image.jpg"`)
+For images located in your `public` directory: use the URL path relative to the `public` directory. (e.g. `src="/images/public-image.jpg"`)
For remote images, provide the full URL. (e.g. `src="https://astro.build/assets/blog/astro-1-release-update.avif"`)
+#### alt
+
+
+
+**Type:** `string`
+**Required:** `true`
+
+
+Defines an alternative text description of the image.
+
+Set to an empty string (alt="") if the image is not a key part of the content (it's decoration or a tracking pixel).
+
#### format
@@ -186,17 +202,23 @@ A `number` can also be provided, useful when the aspect ratio is calculated at b
Source for the original image file.
-For images in your project's repository, use the path relative to the `src` or `public` directory. For remote images, provide the full URL.
+For images located in your project's `src`: use the file path relative to the `src` directory. (e.g. `src="../assets/source-pic.png"`)
+
+For images located in your `public` directory: use the URL path relative to the `public` directory. (e.g. `src="/images/public-image.jpg"`)
+
+For remote images, provide the full URL. (e.g. `src="https://astro.build/assets/blog/astro-1-release-update.avif"`)
#### alt
-If provided, the `alt` string will be included on the built `` element.
+Defines an alternative text description of the image.
+
+Set to an empty string (alt="") if the image is not a key part of the content (it's decoration or a tracking pixel).
#### sizes
@@ -266,7 +288,7 @@ const { src } = await getImage('../assets/hero.png');
-
+
```
@@ -330,19 +352,19 @@ import heroImage from '../assets/hero.png';
---
// optimized image, keeping the original width, height, and image format
-
+
// height will be recalculated to match the original aspect ratio
-
+
// cropping to a specific width and height
-
+
// cropping to a specific aspect ratio and converting to an avif format
-
+
// image imports can also be inlined directly
-
+
```
#### Images in `/public`
@@ -356,11 +378,11 @@ For example, use an image located at `public/social.png` in either static or SSR
```astro title="src/pages/page.astro"
---
import { Image } from '@astrojs/image/components';
-import socialImage from '/social.png';
+import socialImage from '/social.png';
---
// In static builds: the image will be built and optimized to `/dist`.
// In SSR builds: the image will be optimized by the server when requested by a browser.
-
+
```
### Remote images
@@ -375,13 +397,13 @@ const imageUrl = 'https://www.google.com/images/branding/googlelogo/2x/googlelog
---
// cropping to a specific width and height
-
+
// height will be recalculated to match the aspect ratio
-
+
// cropping to a specific height and aspect ratio and converting to an avif format
-
+
```
### Responsive pictures
@@ -401,13 +423,13 @@ const imageUrl = 'https://www.google.com/images/branding/googlelogo/2x/googlelog
---
// Local image with multiple sizes
-
+
// Remote image (aspect ratio is required)
-
+
// Inlined imports are supported
-
+
```
## Troubleshooting
diff --git a/packages/integrations/image/components/Image.astro b/packages/integrations/image/components/Image.astro
index 20efe6ee09b3..1777fffab1c9 100644
--- a/packages/integrations/image/components/Image.astro
+++ b/packages/integrations/image/components/Image.astro
@@ -1,6 +1,7 @@
---
// @ts-ignore
import { getImage } from '../dist/index.js';
+import { warnForMissingAlt } from './index.js';
import type { ImgHTMLAttributes } from './index.js';
import type { ImageMetadata, TransformOptions, OutputFormat } from '../dist/index.js';
@@ -8,10 +9,14 @@ interface LocalImageProps
extends Omit,
Omit {
src: ImageMetadata | Promise<{ default: ImageMetadata }>;
+ /** Defines an alternative text description of the image. Set to an empty string (alt="") if the image is not a key part of the content (it's decoration or a tracking pixel). */
+ alt: string;
}
interface RemoteImageProps extends TransformOptions, astroHTML.JSX.ImgHTMLAttributes {
src: string;
+ /** Defines an alternative text description of the image. Set to an empty string (alt="") if the image is not a key part of the content (it's decoration or a tracking pixel). */
+ alt: string;
format: OutputFormat;
width: number;
height: number;
@@ -21,6 +26,10 @@ export type Props = LocalImageProps | RemoteImageProps;
const { loading = 'lazy', decoding = 'async', ...props } = Astro.props as Props;
+if (props.alt === undefined || props.alt === null) {
+ warnForMissingAlt();
+}
+
const attrs = await getImage(props);
---
diff --git a/packages/integrations/image/components/Picture.astro b/packages/integrations/image/components/Picture.astro
index 36eab92b8740..7fe43d9dbe1b 100644
--- a/packages/integrations/image/components/Picture.astro
+++ b/packages/integrations/image/components/Picture.astro
@@ -1,5 +1,6 @@
---
import { getPicture } from '../dist/index.js';
+import { warnForMissingAlt } from './index.js';
import type { ImgHTMLAttributes, HTMLAttributes } from './index.js';
import type { ImageMetadata, OutputFormat, TransformOptions } from '../dist/index.js';
@@ -8,7 +9,8 @@ interface LocalImageProps
Omit,
Pick {
src: ImageMetadata | Promise<{ default: ImageMetadata }>;
- alt?: string;
+ /** Defines an alternative text description of the image. Set to an empty string (alt="") if the image is not a key part of the content (it's decoration or a tracking pixel). */
+ alt: string;
sizes: HTMLImageElement['sizes'];
widths: number[];
formats?: OutputFormat[];
@@ -19,7 +21,8 @@ interface RemoteImageProps
TransformOptions,
Pick {
src: string;
- alt?: string;
+ /** Defines an alternative text description of the image. Set to an empty string (alt="") if the image is not a key part of the content (it's decoration or a tracking pixel). */
+ alt: string;
sizes: HTMLImageElement['sizes'];
widths: number[];
aspectRatio: TransformOptions['aspectRatio'];
@@ -40,6 +43,10 @@ const {
...attrs
} = Astro.props as Props;
+if (alt === undefined || alt === null) {
+ warnForMissingAlt();
+}
+
const { image, sources } = await getPicture({ src, widths, formats, aspectRatio });
---
diff --git a/packages/integrations/image/components/index.ts b/packages/integrations/image/components/index.ts
index 89d8edd03313..6a8f420ada1e 100644
--- a/packages/integrations/image/components/index.ts
+++ b/packages/integrations/image/components/index.ts
@@ -11,3 +11,17 @@ export type HTMLAttributes = Omit<
astroHTML.JSX.HTMLAttributes,
'client:list' | 'set:text' | 'set:html' | 'is:raw'
>;
+
+let altWarningShown = false;
+
+export function warnForMissingAlt() {
+ if (altWarningShown === true) { return }
+
+ altWarningShown = true;
+
+ console.warn(`\n[@astrojs/image] "alt" text was not provided for an or component.
+
+A future release of @astrojs/image may throw a build error when "alt" text is missing.
+
+The "alt" attribute holds a text description of the image, which isn't mandatory but is incredibly useful for accessibility. Set to an empty string (alt="") if the image is not a key part of the content (it's decoration or a tracking pixel).\n`);
+}
diff --git a/packages/integrations/image/test/fixtures/basic-image/src/pages/index.astro b/packages/integrations/image/test/fixtures/basic-image/src/pages/index.astro
index 4ded85521eac..6b203591b5f5 100644
--- a/packages/integrations/image/test/fixtures/basic-image/src/pages/index.astro
+++ b/packages/integrations/image/test/fixtures/basic-image/src/pages/index.astro
@@ -9,16 +9,16 @@ import { Image } from '@astrojs/image/components';
-
+
-
+
-
+
-
+
-
+
-
+