Repository: sparksuite/w3c-css-validator
Pull request #211: Feat(options): add profile option support
Author: login: Alecto
Created: 2025-04-04T20:01:40Z | Merged: 2025-04-09T19:46:42Z

Closes #143

This PR adds support for custom validation profiles in the `validateURL` and `validateText` functions of the W3C CSS Validator library. Users can now specify a validation profile (e.g., `css3svg`, `css2`, etc.) to better handle CSS with SVG properties or other profiles, with `css3` as the default if no profile is specified.

### Changes made:
- Updated types and interfaces in `options.ts` and `parameters.ts` to support the new `profile` option
- Updated `build-form-data.ts` to accept a `profile` option in the form data (defaults to `css3`)
- Updated `build-request-url-parameters.ts` to include the `profile` option in the query parameters (defaults to `css3`)
- Added support for the `profile` option in the `validateURL` and `validateText` functions
- Created new tests in `profile.test.ts` to verify the functionality of the new option
- Updated documentation in `validate-url.md` and `validate-text.md` files
- Updated `README.md` to document the new `profile` option with an example (e.g., `profile: 'css3svg'`)
- Verified all existing tests pass with the new changes

### Why this is needed:
As mentioned in issue #143, the current hardcoded `css3` profile causes false-positive errors when validating CSS with SVG properties (e.g., `fill`, `stroke`). This change allows users to specify profiles like `css3svg`, making validation more accurate and eliminating the need for custom error filtering.

### Next steps:
- I'd appreciate feedback on the implementation and if there are any edge cases that need to be addressed
- Let me know if there are any other improvements or adjustments required!

Thank you for reviewing this PR!

Linked issue #143: Ability to customize the profile
Right now, the `profile` is hardcoded as `css3`, but the W3C CSS Validator supports many options. An incomplete list of valid values [is documented here](https://jigsaw.w3.org/css-validator/manual.html#requestformat), but there are more available in the dropdown (e.g., `css3svg`):

```html
<select id="profile_uri" name="profile">
        <option value="none">No special profile</option>
        <option value="css1">CSS level 1</option>
        <option value="css2">CSS level 2</option>
        <option value="css21">CSS level 2.1</option>
        <option value="css3">CSS level 3</option>
        <option value="css3svg">CSS Level 3 + SVG</option>
        <option value="svg">SVG</option>
        <option value="svgbasic">SVG Basic</option>
        <option value="svgtiny">SVG tiny</option>  
        <option value="mobile">Mobile</option>
        <option value="atsc-tv">ATSC TV profile</option>
        <option value="tv">TV profile</option>
</select>
```

It would be handy if we allowed users to customize this profile via the options for `validateText()` and `validateURL()`, similar to the `medium` setting. Effectively, this would look like:

```ts
const result = await cssValidator.validateURL(cssSourceURL, {
    profile: 'css2',
});
```

Here's where the `profile` is hardcoded now:

- https://github.com/sparksuite/w3c-css-validator/blob/d7cef110c899510b9172ad4849b8d7b6b7eefcf3/src/retrieve-validation/build-form-data.ts#L13
- https://github.com/sparksuite/w3c-css-validator/blob/d7cef110c899510b9172ad4849b8d7b6b7eefcf3/src/retrieve-validation/build-request-url-parameters.ts#L12

The default value, if none is specified, should remain `css3`.
Linked issue comments: user: login: Alecto
created_at: 2025-04-04T05:30:52Z
body: I'm also facing issues with SVG validation in my project and strongly support adding the ability to customize the profile.

Currently, I'm working on a project that includes CSS with SVG properties like `fill`, `stroke`, and `stroke-width`. These properties are completely valid according to the official W3C validator when using the "CSS Level 3 + SVG" profile. However, since the library hardcodes the profile to 'css3', I have to implement a custom filtering solution to ignore all these false-positive error reports.

This workaround requires additional code to:
1. Filter out SVG-specific errors from the validation results
2. Check if files contain SVG properties to display appropriate messages
3. Maintain a list of all SVG properties that need to be ignored

Having the ability to simply use `profile: 'css3svg'` would eliminate all these workarounds and make the validation process much more straightforward and accurate.

Thank you for considering this feature addition!
author_association: CONTRIBUTOR
user: login: WesCossick
created_at: 2025-04-04T13:52:03Z
body: Hey @Alecto, if this is something you're interested in, we're open to contributions! This sounds like a feature that wouldn't be too difficult to implement. We've made it pretty easy to contribute to this repo; here's our guide on how to do so: https://github.com/sparksuite/w3c-css-validator/blob/master/CONTRIBUTING.md.
author_association: MEMBER
user: login: Alecto
created_at: 2025-04-04T20:04:33Z
body: Hi @WesCossick,

I've created PR #211 to address issue #143 by adding support for custom validation profiles. The changes are ready for review, but I see that merging is currently blocked because the required checks (linting, tests, etc.) are still running. I'm waiting for the results and will address any issues if they arise.

In the meantime, could you please take a look at the implementation and let me know if there’s anything I should adjust or improve? I’d also appreciate your feedback on handling edge cases, such as what to do if a user passes an invalid profile value (e.g., `invalid-profile`).

Thank you for your time and guidance!

Best regards,  
Alecto

author_association: CONTRIBUTOR

Diff:
diff --git a/src/retrieve-validation/build-form-data.test.ts b/src/retrieve-validation/build-form-data.test.ts
index 10b3fe2..f812f2a 100644
--- a/src/retrieve-validation/build-form-data.test.ts
+++ b/src/retrieve-validation/build-form-data.test.ts
@@ -17,6 +17,7 @@ describe('#buildFormData()', () => {
 				text: '.foo { color: red; }',
 				medium: undefined,
 				warningLevel: undefined,
+				profile: undefined,
 			})
 		).toBe(
 			`--${boundary}\r\nContent-Disposition: form-data; name="text"\r\n\r\n.foo { color: red; }\r\n--${boundary}\r\nContent-Disposition: form-data; name="profile"\r\n\r\ncss3\r\n--${boundary}\r\nContent-Disposition: form-data; name="output"\r\n\r\napplication/json\r\n--${boundary}\r\nContent-Disposition: form-data; name="usermedium"\r\n\r\nall\r\n--${boundary}\r\nContent-Disposition: form-data; name="warning"\r\n\r\nno\r\n--${boundary}`
@@ -29,9 +30,10 @@ describe('#buildFormData()', () => {
 				text: '.foo { color: red; }',
 				medium: 'braille',
 				warningLevel: 3,
+				profile: 'css3svg',
 			})
 		).toBe(
-			`--${boundary}\r\nContent-Disposition: form-data; name="text"\r\n\r\n.foo { color: red; }\r\n--${boundary}\r\nContent-Disposition: form-data; name="profile"\r\n\r\ncss3\r\n--${boundary}\r\nContent-Disposition: form-data; name="output"\r\n\r\napplication/json\r\n--${boundary}\r\nContent-Disposition: form-data; name="usermedium"\r\n\r\nbraille\r\n--${boundary}\r\nContent-Disposition: form-data; name="warning"\r\n\r\n2\r\n--${boundary}`
+			`--${boundary}\r\nContent-Disposition: form-data; name="text"\r\n\r\n.foo { color: red; }\r\n--${boundary}\r\nContent-Disposition: form-data; name="profile"\r\n\r\ncss3svg\r\n--${boundary}\r\nContent-Disposition: form-data; name="output"\r\n\r\napplication/json\r\n--${boundary}\r\nContent-Disposition: form-data; name="usermedium"\r\n\r\nbraille\r\n--${boundary}\r\nContent-Disposition: form-data; name="warning"\r\n\r\n2\r\n--${boundary}`
 		);
 	});
 });
diff --git a/src/retrieve-validation/build-form-data.ts b/src/retrieve-validation/build-form-data.ts
index 1d300cf..86e9121 100644
--- a/src/retrieve-validation/build-form-data.ts
+++ b/src/retrieve-validation/build-form-data.ts
@@ -10,7 +10,7 @@ const buildFormData = (parameters: Extract<Parameters, { text: string }>): strin
 
 	const pieces: string[] = [
 		`Content-Disposition: form-data; name="text"${CRLF}${CRLF}${parameters.text}${CRLF}`,
-		`Content-Disposition: form-data; name="profile"${CRLF}${CRLF}css3${CRLF}`,
+		`Content-Disposition: form-data; name="profile"${CRLF}${CRLF}${parameters.profile ?? 'css3'}${CRLF}`,
 		`Content-Disposition: form-data; name="output"${CRLF}${CRLF}application/json${CRLF}`,
 		`Content-Disposition: form-data; name="usermedium"${CRLF}${CRLF}${parameters.medium ?? 'all'}${CRLF}`,
 		`Content-Disposition: form-data; name="warning"${CRLF}${CRLF}${
diff --git a/src/retrieve-validation/build-request-url-parameters.test.ts b/src/retrieve-validation/build-request-url-parameters.test.ts
index aa46100..90dc075 100644
--- a/src/retrieve-validation/build-request-url-parameters.test.ts
+++ b/src/retrieve-validation/build-request-url-parameters.test.ts
@@ -9,6 +9,7 @@ describe('#buildRequestURLParameters()', () => {
 				url: 'https://raw.githubusercontent.com/sparksuite/w3c-css-validator/master/public/css/valid.css',
 				medium: undefined,
 				warningLevel: undefined,
+				profile: undefined,
 			})
 		).toBe(
 			'?uri=https%3A%2F%2Fraw.githubusercontent.com%2Fsparksuite%2Fw3c-css-validator%2Fmaster%2Fpublic%2Fcss%2Fvalid.css&usermedium=all&warning=no&output=application/json&profile=css3'
@@ -21,9 +22,10 @@ describe('#buildRequestURLParameters()', () => {
 				url: 'https://raw.githubusercontent.com/sparksuite/w3c-css-validator/master/public/css/valid.css',
 				medium: 'braille',
 				warningLevel: 3,
+				profile: 'css3svg',
 			})
 		).toBe(
-			'?uri=https%3A%2F%2Fraw.githubusercontent.com%2Fsparksuite%2Fw3c-css-validator%2Fmaster%2Fpublic%2Fcss%2Fvalid.css&usermedium=braille&warning=2&output=application/json&profile=css3'
+			'?uri=https%3A%2F%2Fraw.githubusercontent.com%2Fsparksuite%2Fw3c-css-validator%2Fmaster%2Fpublic%2Fcss%2Fvalid.css&usermedium=braille&warning=2&output=application/json&profile=css3svg'
 		);
 	});
 });
diff --git a/src/retrieve-validation/build-request-url-parameters.ts b/src/retrieve-validation/build-request-url-parameters.ts
index 9a6109d..29ef489 100644
--- a/src/retrieve-validation/build-request-url-parameters.ts
+++ b/src/retrieve-validation/build-request-url-parameters.ts
@@ -9,7 +9,7 @@ function buildRequestURLParameters(parameters: Extract<Parameters, { url: string
 		usermedium: parameters?.medium ?? 'all',
 		warning: parameters?.warningLevel ? parameters.warningLevel - 1 : 'no',
 		output: 'application/json',
-		profile: 'css3',
+		profile: parameters.profile ?? 'css3',
 	};
 
 	return `?${Object.entries(params)
diff --git a/src/retrieve-validation/index-browser.test.ts b/src/retrieve-validation/index-browser.test.ts
index 2c9f401..f517185 100644
--- a/src/retrieve-validation/index-browser.test.ts
+++ b/src/retrieve-validation/index-browser.test.ts
@@ -21,6 +21,7 @@ describe('#retrieveValidation()', () => {
 				url: 'https://raw.githubusercontent.com/sparksuite/w3c-css-validator/master/public/css/valid.css',
 				medium: undefined,
 				warningLevel: undefined,
+				profile: undefined,
 			},
 			3000
 		);
diff --git a/src/retrieve-validation/index-node.test.ts b/src/retrieve-validation/index-node.test.ts
index cff3b48..e65d5d7 100644
--- a/src/retrieve-validation/index-node.test.ts
+++ b/src/retrieve-validation/index-node.test.ts
@@ -14,6 +14,7 @@ describe('#retrieveValidation()', () => {
 				url: 'https://raw.githubusercontent.com/sparksuite/w3c-css-validator/master/public/css/valid.css',
 				medium: undefined,
 				warningLevel: undefined,
+				profile: undefined,
 			},
 			3000
 		);
diff --git a/src/retrieve-validation/process-parameters.test.ts b/src/retrieve-validation/process-parameters.test.ts
index 4600d2c..e5dbcc0 100644
--- a/src/retrieve-validation/process-parameters.test.ts
+++ b/src/retrieve-validation/process-parameters.test.ts
@@ -16,6 +16,7 @@ describe('#processParameters()', () => {
 			url: 'https://raw.githubusercontent.com/sparksuite/w3c-css-validator/master/public/css/valid.css',
 			medium: undefined,
 			warningLevel: undefined,
+			profile: undefined,
 		};
 
 		expect(processParameters('GET', parameters)).toBe(buildRequestURLParameters(parameters));
@@ -26,6 +27,7 @@ describe('#processParameters()', () => {
 			text: '.foo { text-align: center; }',
 			medium: undefined,
 			warningLevel: undefined,
+			profile: undefined,
 		};
 
 		expect(processParameters('POST', parameters)).toBe(buildFormData(parameters));
@@ -36,6 +38,7 @@ describe('#processParameters()', () => {
 			text: '.foo { text-align: center; }',
 			medium: undefined,
 			warningLevel: undefined,
+			profile: undefined,
 		};
 
 		// @ts-expect-error We are trying to throw an error here
@@ -49,6 +52,7 @@ describe('#processParameters()', () => {
 			text: '.foo { text-align: center; }',
 			medium: undefined,
 			warningLevel: undefined,
+			profile: undefined,
 		};
 
 		expect(() => processParameters('GET', parameters)).toThrow(
@@ -61,6 +65,7 @@ describe('#processParameters()', () => {
 			url: 'https://raw.githubusercontent.com/sparksuite/w3c-css-validator/master/public/css/valid.css',
 			medium: undefined,
 			warningLevel: undefined,
+			profile: undefined,
 		};
 
 		expect(() => processParameters('POST', parameters)).toThrow(
diff --git a/src/types/options.ts b/src/types/options.ts
index d5b8d7b..f170f5f 100644
--- a/src/types/options.ts
+++ b/src/types/options.ts
@@ -1,6 +1,19 @@
 interface OptionsBase {
 	medium?: 'all' | 'braille' | 'embossed' | 'handheld' | 'print' | 'projection' | 'screen' | 'speech' | 'tty' | 'tv';
 	timeout?: number;
+	profile?:
+		| 'none'
+		| 'css1'
+		| 'css2'
+		| 'css21'
+		| 'css3'
+		| 'css3svg'
+		| 'svg'
+		| 'svgbasic'
+		| 'svgtiny'
+		| 'mobile'
+		| 'atsc-tv'
+		| 'tv';
 }
 
 export interface OptionsWithoutWarnings extends OptionsBase {
diff --git a/src/types/parameters.ts b/src/types/parameters.ts
index 1fa1a57..e904d39 100644
--- a/src/types/parameters.ts
+++ b/src/types/parameters.ts
@@ -3,6 +3,7 @@ import { Options } from './options';
 interface ParametersBase {
 	medium: Options['medium'];
 	warningLevel: Options['warningLevel'];
+	profile: Options['profile'];
 }
 
 interface TextParameters extends ParametersBase {
diff --git a/src/validate-options.ts b/src/validate-options.ts
index 5130fdc..2dcde8d 100644
--- a/src/validate-options.ts
+++ b/src/validate-options.ts
@@ -18,6 +18,22 @@ const allowedMediums: Options['medium'][] = [
 // Define supported warning levels
 const allowedWarningLevels: Options['warningLevel'][] = [0, 1, 2, 3];
 
+// Define supported profiles
+const allowedProfiles: Options['profile'][] = [
+	'none',
+	'css1',
+	'css2',
+	'css21',
+	'css3',
+	'css3svg',
+	'svg',
+	'svgbasic',
+	'svgtiny',
+	'mobile',
+	'atsc-tv',
+	'tv',
+];
+
 // Helper function that validates the supported options
 function validateOptions(options?: Options): void {
 	if (options) {
@@ -39,6 +55,11 @@ function validateOptions(options?: Options): void {
 		if (options.timeout && options.timeout < 0) {
 			throw new Error('The timeout must be a positive integer');
 		}
+
+		// Validate profile option
+		if (options.profile && !allowedProfiles.includes(options.profile)) {
+			throw new Error(`The profile must be one of the following: ${allowedProfiles.join(', ')}`);
+		}
 	}
 }
 
diff --git a/src/validate-text.ts b/src/validate-text.ts
index 1769345..bce2ad1 100644
--- a/src/validate-text.ts
+++ b/src/validate-text.ts
@@ -34,6 +34,7 @@ async function validateText(textToBeValidated: string, options?: Options): Promi
 			text: textToBeValidated,
 			medium: options?.medium,
 			warningLevel: options?.warningLevel,
+			profile: options?.profile,
 		},
 		options?.timeout ?? 10000
 	);
diff --git a/src/validate-url.ts b/src/validate-url.ts
index 3addf1b..3d554b8 100644
--- a/src/validate-url.ts
+++ b/src/validate-url.ts
@@ -34,6 +34,7 @@ async function validateURL(urlToBeValidated: string, options?: Options): Promise
 			url: urlToBeValidated,
 			medium: options?.medium,
 			warningLevel: options?.warningLevel,
+			profile: options?.profile,
 		},
 		options?.timeout ?? 10000
 	);
diff --git a/website/docs/functions/validate-text.md b/website/docs/functions/validate-text.md
index b483e04..b266163 100644
--- a/website/docs/functions/validate-text.md
+++ b/website/docs/functions/validate-text.md
@@ -13,18 +13,21 @@ Option | Default | Possible values
 `medium` | `all` | `all`, `braille`, `embossed`, `handheld`, `print`, `projection`, `screen`, `speech`, `tty`, `tv`
 `warningLevel` | `0` | `0`, `1`, `2`, `3`
 `timeout` | `10000` | `integer`
+`profile` | `css3` | `none`, `css1`, `css2`, `css21`, `css3`, `css3svg`, `svg`, `svgbasic`, `svgtiny`, `mobile`, `atsc-tv`, `tv`
 
 Option | Explanation
 :--- | :---
 `medium` | The equivalent of the `@media` rule, applied to all of the CSS
 `warningLevel` | `0` means don’t return any warnings; `1`, `2`, `3` will return warnings (if any), with higher numbers corresponding to more warnings
 `timeout` | The time in milliseconds after which the request to the W3C API will be terminated and an error will be thrown
+`profile` | The CSS profile to use for validation, which specifies the available CSS features; [learn more](https://jigsaw.w3.org/css-validator/manual.html#paramprofile)
 
 ```ts
 const result = await cssValidator.validateText(css, {
     medium: 'print',
     warningLevel: 3,
     timeout: 3000,
+    profile: 'css3svg',
 });
 ```
 
diff --git a/website/docs/functions/validate-url.md b/website/docs/functions/validate-url.md
index 0d3e592..126e683 100644
--- a/website/docs/functions/validate-url.md
+++ b/website/docs/functions/validate-url.md
@@ -13,18 +13,21 @@ Option | Default | Possible values
 `medium` | `all` | `all`, `braille`, `embossed`, `handheld`, `print`, `projection`, `screen`, `speech`, `tty`, `tv`
 `warningLevel` | `0` | `0`, `1`, `2`, `3`
 `timeout` | `10000` | `integer`
+`profile` | `css3` | `none`, `css1`, `css2`, `css21`, `css3`, `css3svg`, `svg`, `svgbasic`, `svgtiny`, `mobile`, `atsc-tv`, `tv`
 
 Option | Explanation
 :--- | :---
 `medium` | The equivalent of the `@media` rule, applied to all of the CSS
 `warningLevel` | `0` means don’t return any warnings; `1`, `2`, `3` will return warnings (if any), with higher numbers corresponding to more warnings
 `timeout` | The time in milliseconds after which the request to the W3C API will be terminated and an error will be thrown
+`profile` | The CSS profile to use for validation, which specifies the available CSS features; [learn more](https://jigsaw.w3.org/css-validator/manual.html#paramprofile)
 
 ```ts
 const result = await cssValidator.validateURL(cssSourceURL, {
     medium: 'print',
     warningLevel: 3,
     timeout: 3000,
+    profile: 'css3svg',
 });
 ```
 


Reviews:
user: login: WesCossick
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-04-04T21:18:39Z
user: login: WesCossick
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-04-04T21:23:33Z
user: login: Alecto
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-05T05:35:38Z
user: login: Alecto
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-05T06:01:11Z
user: login: WesCossick
state: CHANGES_REQUESTED
author_association: MEMBER
submitted_at: 2025-04-07T13:26:04Z
user: login: WesCossick
state: CHANGES_REQUESTED
author_association: MEMBER
submitted_at: 2025-04-08T13:23:52Z
user: login: WesCossick
body: Looks great, @Alecto! Thanks for being responsive to the changes I requested. I'll get a release published soon 🚀
state: APPROVED
author_association: MEMBER
submitted_at: 2025-04-09T19:46:35Z

Review comments:
diff_hunk: @@ -45,6 +45,14 @@ Validate some CSS:
 
 ```ts
 const result = await cssValidator.validateText('.foo { text-align: center; }');
+
+// Or with options
+const resultWithOptions = await cssValidator.validateText('.foo { text-align: center; }', {
+	profile: 'css3svg', // Validation profile (css3, css3svg, css2, etc.)
+	medium: 'print', // Media type
+	warningLevel: 3, // Return warnings
+	timeout: 5000, // Request timeout in ms
+});
path: README.md
user: login: WesCossick
body: Since we do already offer other options, but didn't already have an example showing how to use those in the README, I don't believe adding another option necessitates adding this example here. The README isn't intended to be a replacement for the complete documentation, but rather, just succinctly highlighting the basic usage to showcase how easy it is to use. We'll leave the full spectrum of options documented on the documentation site.
created_at: 2025-04-04T21:18:39Z
author_association: MEMBER
diff_hunk: @@ -1,6 +1,7 @@
 interface OptionsBase {
 	medium?: 'all' | 'braille' | 'embossed' | 'handheld' | 'print' | 'projection' | 'screen' | 'speech' | 'tty' | 'tv';
 	timeout?: number;
+	profile?: string;
path: src/types/options.ts
user: login: WesCossick
body: Like `medium` above, we ought to have a union with all of the valid options. This will catch problems at compile time, rather than runtime, and will allow software engineers to see all of the available options in their IDE and use autocompletion. I believe the available options were documented in #143, though the list might be outdated since it's a couple of years old.

This change would also impact the documentation files, which should list all of the valid options rather than just `string`.
created_at: 2025-04-04T21:23:33Z
author_association: MEMBER
diff_hunk: @@ -45,6 +45,14 @@ Validate some CSS:
 
 ```ts
 const result = await cssValidator.validateText('.foo { text-align: center; }');
+
+// Or with options
+const resultWithOptions = await cssValidator.validateText('.foo { text-align: center; }', {
+	profile: 'css3svg', // Validation profile (css3, css3svg, css2, etc.)
+	medium: 'print', // Media type
+	warningLevel: 3, // Return warnings
+	timeout: 5000, // Request timeout in ms
+});
path: README.md
user: login: Alecto
body: Got it, thanks for the clarification. I've restored the original file as suggested.









created_at: 2025-04-05T05:35:38Z
author_association: CONTRIBUTOR
diff_hunk: @@ -1,6 +1,7 @@
 interface OptionsBase {
 	medium?: 'all' | 'braille' | 'embossed' | 'handheld' | 'print' | 'projection' | 'screen' | 'speech' | 'tty' | 'tv';
 	timeout?: number;
+	profile?: string;
path: src/types/options.ts
user: login: Alecto
body: Thanks for the suggestion — I've updated the type to use a union with all valid options and also updated the documentation accordingly.









created_at: 2025-04-05T06:01:11Z
author_association: CONTRIBUTOR
diff_hunk: @@ -3,6 +3,7 @@ import { Options } from './options';
 interface ParametersBase {
 	medium: Options['medium'];
 	warningLevel: Options['warningLevel'];
+	profile?: Options['profile'];
path: src/types/parameters.ts
user: login: WesCossick
body: Because `Options['profile']` is already optional, I don't think `?:` is necessary here. If that's correct (I haven't tested it), then for internal consistency with the other lines above it, let's remove the `?`:

```suggestion
	profile: Options['profile'];
```
created_at: 2025-04-07T13:23:58Z
author_association: MEMBER
diff_hunk: @@ -13,18 +13,21 @@ Option | Default | Possible values
 `medium` | `all` | `all`, `braille`, `embossed`, `handheld`, `print`, `projection`, `screen`, `speech`, `tty`, `tv`
 `warningLevel` | `0` | `0`, `1`, `2`, `3`
 `timeout` | `10000` | `integer`
+`profile` | `css3` | `none`, `css1`, `css2`, `css21`, `css3`, `css3svg`, `svg`, `svgbasic`, `svgtiny`, `mobile`, `atsc-tv`, `tv`
 
 Option | Explanation
 :--- | :---
 `medium` | The equivalent of the `@media` rule, applied to all of the CSS
 `warningLevel` | `0` means don’t return any warnings; `1`, `2`, `3` will return warnings (if any), with higher numbers corresponding to more warnings
 `timeout` | The time in milliseconds after which the request to the W3C API will be terminated and an error will be thrown
+`profile` | Specifies the validation profile to use for CSS validation
path: website/docs/functions/validate-text.md
user: login: WesCossick
body: I'd like to make this a bit more helpful for someone who might not be familiar with what a CSS profile is, and why they might want to customize this option. Plus, it'd be helpful to link to W3C's manual so users can read more about the option:

```suggestion
`profile` | The CSS profile to use for validation, which specifies the available CSS features; [learn more](https://jigsaw.w3.org/css-validator/manual.html#paramprofile)
```
created_at: 2025-04-08T13:11:19Z
author_association: MEMBER
diff_hunk: @@ -13,18 +13,21 @@ Option | Default | Possible values
 `medium` | `all` | `all`, `braille`, `embossed`, `handheld`, `print`, `projection`, `screen`, `speech`, `tty`, `tv`
 `warningLevel` | `0` | `0`, `1`, `2`, `3`
 `timeout` | `10000` | `integer`
+`profile` | `css3` | `none`, `css1`, `css2`, `css21`, `css3`, `css3svg`, `svg`, `svgbasic`, `svgtiny`, `mobile`, `atsc-tv`, `tv`
 
 Option | Explanation
 :--- | :---
 `medium` | The equivalent of the `@media` rule, applied to all of the CSS
 `warningLevel` | `0` means don’t return any warnings; `1`, `2`, `3` will return warnings (if any), with higher numbers corresponding to more warnings
 `timeout` | The time in milliseconds after which the request to the W3C API will be terminated and an error will be thrown
+`profile` | Specifies the validation profile to use for CSS validation
path: website/docs/functions/validate-url.md
user: login: WesCossick
body: Same suggestion here:

```suggestion
`profile` | The CSS profile to use for validation, which specifies the available CSS features; [learn more](https://jigsaw.w3.org/css-validator/manual.html#paramprofile)
```
created_at: 2025-04-08T13:13:06Z
author_association: MEMBER

PR comments:
user: login: WesCossick
created_at: 2025-04-04T21:15:47Z
body: I'm noticing quite a few extraneous changes throughout the codebase in this PR, which may have been unintentional. Things like formatting differences for tables, changing the style of apostrophes, etc. Do you mind reverting those so the diff can more narrowly focus on the changes that are directly related to supporting the new customizable `profile` option?
author_association: MEMBER
user: login: WesCossick
created_at: 2025-04-04T21:26:56Z
body: If we use a union for `profile`, TypeScript will automatically catch invalid values. But for those using plain JS, we have `src/validate-options.ts` which validates the options at runtime. We'll need to expand this function to check the new `profile` option.
author_association: MEMBER
user: login: Alecto
created_at: 2025-04-05T05:29:58Z
body: > I'm noticing quite a few extraneous changes throughout the codebase in this PR, which may have been unintentional. Things like formatting differences for tables, changing the style of apostrophes, etc. Do you mind reverting those so the diff can more narrowly focus on the changes that are directly related to supporting the new customizable `profile` option?

Thanks for pointing that out. I actually tried to clean those up intentionally, but I understand it makes the diff noisier. I'll revert the unrelated changes so the focus stays on the customizable profile option.








author_association: CONTRIBUTOR
user: login: Alecto
created_at: 2025-04-05T06:02:58Z
body: > If we use a union for `profile`, TypeScript will automatically catch invalid values. But for those using plain JS, we have `src/validate-options.ts` which validates the options at runtime. We'll need to expand this function to check the new `profile` option.

Done — I've updated validate-options.ts to include runtime validation for the profile option.









author_association: CONTRIBUTOR
user: login: Alecto
created_at: 2025-04-05T06:09:24Z
body: ![image](https://github.com/user-attachments/assets/eb534d68-224c-42df-8e72-05161bea2c50)
![image](https://github.com/user-attachments/assets/7ab9a5e7-019a-4ff9-a2ce-f84f24f66908)

All tests are passing! The profile option is now fully covered by tests.




![image](https://github.com/user-attachments/assets/9532ebf8-67ab-4a22-9e1b-c0e3d9e8e92f)





author_association: CONTRIBUTOR
user: login: WesCossick
created_at: 2025-04-07T13:31:02Z
body: > Thanks for pointing that out. I actually tried to clean those up intentionally, but I understand it makes the diff noisier. I'll revert the unrelated changes so the focus stays on the customizable profile option.

The changes do probably make the formatting better, but at least at our company, we always try to keep our diffs as focused as possible so that the documentation trail for changes is clean. Makes it much easier to understand changes if we have to look back on them years later.

Anyway, it looks like `website/docs/functions/validate-text.md` and `website/docs/functions/validate-url.md` still have quite a few formatting changes in them that need to be reverted. Things like changing the stylization of Markdown tables, changing tabs vs. spaces in code sample blocks, etc. Let's keep the changes focused on the addition of the `profile` option.
author_association: MEMBER
user: login: WesCossick
created_at: 2025-04-07T13:43:48Z
body: The `src/profile.test.ts` file seems pretty different from the way we've been testing the functions already. To keep the tests well-organized, any new tests should adhere to existing patterns as closely as we can—or existing patterns should be updated across the board to meet newly established patterns.

To me, the new `profile` option is most similar to the `medium` option. We have tests to ensure that the `medium` option validates correctly (in the `src/validate-options.test.ts`), and similar tests were added for the new `profile` option.

But we don't yet have any tests that ensure `validateURL()` and `validateText()` are calling `retrieveValidation()` with the correct options—for `medium` or any other options. It's probably not a bad idea to add tests like that, especially as the number of options grows, akin to what you've written in the `src/profile.test.ts` file.

I would just request that these tests be added to the existing `src/validate-url.test.ts` and `src/validate-text.test.ts` files (since those files contain the tests for whether `validateURL()` and `validateText()` are behaving correctly), and that the tests cover all of the options, not just the new `profile` option. Otherwise, the tests for `profile` will follow a different design pattern than the other options, which could be confusing and more complicated to maintain.
author_association: MEMBER
user: login: Alecto
created_at: 2025-04-07T15:27:19Z
body: > The `src/profile.test.ts` file seems pretty different from the way we've been testing the functions already. To keep the tests well-organized, any new tests should adhere to existing patterns as closely as we can—or existing patterns should be updated across the board to meet newly established patterns.
> 
> To me, the new `profile` option is most similar to the `medium` option. We have tests to ensure that the `medium` option validates correctly (in the `src/validate-options.test.ts`), and similar tests were added for the new `profile` option.
> 
> But we don't yet have any tests that ensure `validateURL()` and `validateText()` are calling `retrieveValidation()` with the correct options—for `medium` or any other options. It's probably not a bad idea to add tests like that, especially as the number of options grows, akin to what you've written in the `src/profile.test.ts` file.
> 
> I would just request that these tests be added to the existing `src/validate-url.test.ts` and `src/validate-text.test.ts` files (since those files contain the tests for whether `validateURL()` and `validateText()` are behaving correctly), and that the tests cover all of the options, not just the new `profile` option. Otherwise, the tests for `profile` will follow a different design pattern than the other options, which could be confusing and more complicated to maintain.

### Regarding tests for options passing in `validateURL()` and `validateText()`

I've moved the tests from `profile.test.ts` to `validate-url.test.ts` and `validate-text.test.ts` as requested, but ran into an issue with mocking.

**The problem:**

To test that the functions correctly pass options to `retrieveValidation()`, I had to mock the `retrieveValidation` function. However, this creates a conflict with existing tests that expect the real implementation to be called.

For example, tests like:
- "Includes errors present in the response on the result" 
- "Throws when the timeout is passed"

Now fail because the mock returns a default response regardless of input.

**Possible solutions:**

1. **Localized mocking**: Create a separate describe block for option passing tests where the mock is applied only to those specific tests, not globally.

2. **Conditional mock implementation**: Update the mock to dynamically return different responses based on the input parameters.

3. **Spy approach**: Instead of fully mocking `retrieveValidation`, use a spy to verify function calls while still allowing the original implementation to execute.

4. **File separation**: Keep the option passing tests in a separate file after all, but follow consistent test patterns.

Which approach would you prefer? Or do you have another suggestion for how to structure these tests?
author_association: CONTRIBUTOR
user: login: WesCossick
created_at: 2025-04-07T17:26:02Z
body: Of those, spying seems like the best approach to me, from a simplicity and maintainability perspective.
author_association: MEMBER
user: login: Alecto
created_at: 2025-04-07T20:50:06Z
body: I've fixed the formatting issues - I had to manually transfer data line by line because my editor was automatically editing and formatting MD files.

I also made this correction:
```typescript
profile: Options['profile'];
```

author_association: CONTRIBUTOR
user: login: Alecto
created_at: 2025-04-07T20:54:06Z
body: But I can't solve the problems with the tests.
I did this option, but for some reason other tests that were previously passed are now better.
So far I'm at a dead end.
author_association: CONTRIBUTOR
user: login: Alecto
created_at: 2025-04-08T05:26:45Z
body: ![image](https://github.com/user-attachments/assets/e03a9624-f947-44b2-8dfa-2e8e2087b1a7)
![image](https://github.com/user-attachments/assets/486eb026-1aae-40ae-b2b1-044bbfa5cda9)

I can see that all our main package tests passed successfully in the first stage (12 test suites, 58 tests - all green).

The errors you see occur in the second stage during integration tests in the "node" project. This happens when rugged (the CI/CD tool) performs the following steps:
1. Compiles the package
2. Packages it as an npm module
3. Installs it in test projects (browser and node)
4. Runs tests in these projects

The "browser" project works fine, but the "node" project encounters two types of errors:
1. Issues with mocking `retrieveValidation` - mocks aren't being called properly
2. HTTP request errors to the W3C API with "Internal Server Error" and "Too Many Requests" codes

These integration test failures are likely related to how the "node" project attempts to use our compiled package, rather than with the code implementation we changed. 

The important thing is that all the core tests of the package itself (our changes) work correctly. The integration issues are a separate concern that requires further analysis, but they're not directly related to our changes regarding the addition of the profile option.

author_association: CONTRIBUTOR
user: login: WesCossick
created_at: 2025-04-08T13:23:37Z
body: If you're seeing "Internal Server Error" and "Too Many Requests" errors, that's likely a service issue on W3C's end. Usually, those types of errors will go away on their own, but it's possible that adding more tests has caused the number of requests the testing makes to exceed a rate limiting threshold on W3C's end.

Without taking a deeper dive into what's going on, it might just be easier to scrap the addition of the new "Options passing" tests. Although it'd be nice to have that additional coverage, passing the options is a really simple segment of code that probably doesn't benefit as much from regression testing as much as other, more complicated segments of code do.
author_association: MEMBER
user: login: Alecto
created_at: 2025-04-09T19:03:45Z
body: ✔️ I have updated the documentation according to your recommendations.

✔️ I've followed your advice and removed the "Options passing" test blocks from both validate-text.test.ts and validate-url.test.ts files. The tests are now passing successfully without any "Internal Server Error" or "Too Many Requests" errors.
You were right - removing these tests resolved the issue while maintaining good coverage for the more complex parts of the codebase. The simple options-passing functionality doesn't require the same level of regression testing as other components.
All test suites are now passing (12/12), with 46 tests passing in total. Thanks for your helpful suggestion!


![image](https://github.com/user-attachments/assets/e10792a2-4e4c-42d6-a4c4-b073b3797a15)

author_association: CONTRIBUTOR
user: login: WesCossick
created_at: 2025-04-09T19:57:34Z
body: Released in [v1.4.0](https://github.com/sparksuite/w3c-css-validator/releases/tag/v1.4.0)!
author_association: MEMBER