I understand the appeal of a browser textbox when someone hands you a wall of markup. Paste, click, breathe again. But formatting alone cannot prove that HTML is valid, CSS behaves correctly, or JSON has the shape your application expects—and sending private source to an unknown service can create a security incident. A small local toolchain is safer and repeatable.
Four jobs that are often confused
A formatter rewrites whitespace and layout to a consistent style.
A linter reports suspicious, invalid, inaccessible, or policy-breaking patterns.
A validator/parser checks whether input follows a syntax; schema validation also checks structure and constraints.
A minifier removes delivery bytes and may transform syntax while aiming to preserve behavior.
A formatter is not automatically a validator, and a successful minifier is not proof that the result works in every target browser.
A practical local stack
Prettier for deterministic HTML, CSS, and JSON formatting.
HTML-validate for configurable HTML linting and structural rules.
Stylelint for CSS correctness and project conventions.
jqor Python’s standard-library JSON tool for strict JSON parsing and readable output.Ajv or another standards-aware validator when JSON must satisfy a JSON Schema.
A reviewed bundler/minifier such as cssnano in the production build—not an ad hoc copy/paste step.
1. Create a project-owned tool environment
npm init -y
npm install --save-dev --save-exact prettier html-validate stylelint stylelint-config-standardadded development dependencies and updated package-lock.jsonRisk level: caution. Review the command before running it.
Review the dependency change before committing
--save-devrecords build-quality tools as development dependencies.--save-exactavoids a range inpackage.json; the lockfile captures the resolved dependency graph.npm init -ycreates a manifest and should not overwrite an established project setup.Inspect package provenance, licenses, install scripts, vulnerability reports, and the lockfile diff under your organization’s supply-chain policy.
CI should use the project lockfile, commonly through
npm ci, rather than resolving fresh versions.
2. Format HTML, CSS, and JSON with Prettier
{
"printWidth": 100,
"singleQuote": true,
"tabWidth": 2,
"useTabs": false
}Keep formatting policy intentionally small
The config is valid JSON, so property names and strings use double quotes.
printWidthis a wrapping preference, not a hard maximum for every construct.Prettier may normalize embedded CSS/JavaScript when it can parse the surrounding file.
Avoid turning every preference into debate; formatter value comes from deterministic automation.
Ignore generated, vendor, minified, and other non-source files with a reviewed
.prettierignore.
npx prettier --check "src/**/*.{html,css,json}"
npx prettier --write "src/**/*.{html,css,json}"Checking formatting...
All matched files use Prettier code style!Risk level: caution. Review the command before running it.
Check in CI; write in development
--checkexits nonzero when matched files need formatting and does not rewrite them.--writemutates every matched source file, so run it with a clean/known worktree and review the diff.The quoted glob is passed to Prettier rather than expanded inconsistently by the shell.
Formatting cannot identify every missing semantic element, accessibility error, or invalid data relationship.
Do not run a formatter blindly over generated snapshots, vendor files, or templates whose syntax needs a plugin not yet reviewed.
3. Lint HTML structure and accessibility hooks
{
"extends": ["html-validate:recommended"],
"rules": {
"attr-quotes": ["error", { "style": "double" }],
"no-dup-id": "error"
}
}Lint rules turn silent browser recovery into feedback
Browsers recover from many malformed constructs, so “it rendered” is weak evidence.
The recommended preset supplies a maintained baseline; project overrides should be documented.
Duplicate IDs can break fragments, form labels, ARIA references, tests, and JavaScript selection.
Static linting cannot prove dynamic templates render unique IDs across repeated component instances.
Framework templates may require a transformer/plugin or linting the rendered HTML rather than raw template syntax.
npx html-validate "src/**/*.html"0 errors foundTreat each diagnostic as a source location
The command reads matched HTML files and exits nonzero on configured errors.
Fix the markup rather than disabling a rule globally after one inconvenient result.
If a rule is inapplicable, scope the exception narrowly and record why.
Pair linting with browser accessibility testing, keyboard interaction, and assistive-technology review.
Validate fully rendered pages for server/client systems that assemble markup dynamically.
4. Lint CSS before minifying it
{
"extends": ["stylelint-config-standard"],
"rules": {
"declaration-block-no-duplicate-properties": true,
"selector-id-pattern": null
}
}Start from a standard, then encode project decisions
Stylelint catches invalid and contradictory declarations that formatting may leave untouched.
A shared config reduces bespoke rule maintenance.
Setting a rule to
nulldisables it deliberately; remove unnecessary overrides rather than accumulating them.CSS preprocessors, CSS Modules, and framework syntax may need appropriate syntax packages/configuration.
Lint success does not prove visual layout across viewport, browser, theme, and content variations.
npx stylelint "src/**/*.css"
npx stylelint "src/**/*.css" --fixNo Stylelint errorsRisk level: caution. Review the command before running it.
Auto-fix is still a code change
The first command reports without intentional rewriting.
--fixchanges files for fixable rules; use it only after the reporting pass and review the diff.Some fixes can expose cascade assumptions even when syntactically safe.
Run visual/component tests after broad CSS changes.
Keep linting on authored source; minified output is a build artifact.
5. Parse JSON locally
jq empty config.json
jq --sort-keys . config.json > /tmp/config.formatted.json
python -m json.tool config.json > /dev/nullNo output and exit status 0 means the parser accepted the JSON.Syntax validation is not schema validation
jq emptyparses the complete document without printing its data.jq --sort-keys .writes a formatted copy to an explicit temporary file rather than overwriting source.Python’s
json.tooloffers a standard-library parser whenjqis unavailable.Strict JSON does not allow comments, trailing commas, single-quoted strings,
NaN, orInfinity.A syntactically valid payload can still omit required fields, use the wrong types, or violate business rules.
6. Validate JSON against a schema
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"type": "object",
"required": ["environment", "features"],
"properties": {
"environment": { "enum": ["development", "staging", "production"] },
"features": {
"type": "array",
"items": { "type": "string", "minLength": 1 },
"uniqueItems": true
}
},
"additionalProperties": false
}The schema describes structure, not every domain invariant
Draft 2020-12 is declared explicitly so validator behavior is not guessed.
requiredcontrols property presence; listing a property underpropertiesalone does not require it.enumlimits the environment to known string values.uniqueItemsprevents duplicate feature values under JSON equality semantics.additionalProperties:falserejects misspelled or unexpected keys but can make schema evolution stricter; choose intentionally.
npx ajv-cli validate \
--spec=draft2020 \
--schema config.schema.json \
--data config.jsonconfig.json validPin the validator before relying on it
Add a reviewed Ajv CLI version to project development dependencies before using the command in CI.
--spec=draft2020aligns the validator with the schema declaration.A schema-valid configuration may still reference nonexistent resources or violate cross-system policy.
Test valid and intentionally invalid fixtures to prove the schema rejects what matters.
Avoid placing secrets in test fixtures, command history, or CI logs.
7. Minify CSS in a production build
module.exports = ({ env }) => ({
plugins: [
require("autoprefixer"),
...(env === "production" ? [require("cssnano")({ preset: "default" })] : []),
],
});Minification belongs after authored-source checks
The example enables cssnano only for the production environment.
Autoprefixer derives prefixes from the project’s browsers target configuration.
Add and pin these packages before using this configuration.
Preserve source maps according to debugging and source-disclosure policy.
Review transformations when CSS relies on custom properties, advanced functions, hacks, or third-party code.
NODE_ENV=production npx postcss src/styles.css \
--output dist/styles.min.css
wc -c src/styles.css dist/styles.min.css8421 src/styles.css
5310 dist/styles.min.cssRisk level: caution. Review the command before running it.
A smaller file is only the first assertion
The command writes a build artifact under
dist; confirm that directory is intended and generated.Byte counts show raw size, not transfer size after Brotli/gzip.
Run visual regression, supported-browser, and critical-interaction tests against the minified bundle.
Verify URLs and source-map references resolve from the deployed asset location.
Use content hashes and long-lived caching through the established asset pipeline rather than manual filename editing.
Formatters cannot repair ambiguous HTML
<ul>
<li>First item
<li>Second item
</ul>
<div><p>Browser recovery can make this look acceptable.</div>Parsing rules and author intent are different
Some end tags are optional in HTML, so a formatter may produce valid output that still surprises a reader.
Browsers use specified error-recovery algorithms for malformed nesting.
A formatter cannot know whether a missing wrapper or closing tag reflects intended structure.
Inspect the DOM in browser developer tools and validate/lint the rendered markup.
Add semantic, accessibility, and behavior tests rather than using indentation as proof.
Editor and pre-commit workflow
Enable format-on-save only with the repository’s selected formatter and configuration.
Run linting on changed files for fast feedback, then the full check in CI.
Do not let editor extensions silently use globally installed tool versions.
Keep generated/minified files excluded unless the repository intentionally commits them.
Make CI commands identical to local package scripts so failures reproduce.
Use pre-commit hooks as convenience; protected CI remains the enforcement boundary.
Online tool decision checklist
Does the input contain credentials, personal data, customer content, internal URLs, or proprietary source? Keep it local.
Is the service vendor approved, and are transport, retention, training, subprocessors, and deletion terms understood?
Can the same result be reproduced later with a named tool/version/configuration?
Will the tool upload data as you type or only after submission?
Does the output need validation/tests beyond visual formatting?
Can a small synthetic example reproduce the issue without exposing the real file? Use that instead.
Troubleshooting map
Prettier says “no parser”: verify the file extension, explicit parser, or required reviewed plugin.
HTML linter rejects template syntax: configure the appropriate transformer or lint rendered HTML.
Stylelint cannot parse a preprocessor: select the matching custom syntax and compatible rules.
JSON formatter accepts data but the app rejects it: validate schema, encoding, number ranges, and application semantics.
jq reports an error near a quote: inspect escaping and remember JSON requires double-quoted keys/strings.
Ajv rejects a valid-looking file: align schema draft, formats, references, and strictness options.
Minified CSS changes the page: compare the transformation, browser targets, cascade, asset URLs, and source maps; do not ship solely because the build exited zero.
CI and editor disagree: compare resolved package versions, configuration discovery, ignore files, line endings, and working directory.
A reliable completion gate
Dependencies and lockfile are reviewed and committed.
Formatting check produces no diff.
HTML and CSS linters pass on authored or correctly transformed source.
JSON files parse strictly and schema-bound files satisfy the intended draft.
Secret scanning and data-handling policy permit every processed fixture.
Minification is confined to production artifacts and preserves source files.
Rendered HTML, accessibility, visual behavior, and supported browsers are tested.
CI runs the same pinned commands from a clean checkout.
Production asset URLs, content types, compression, caching, CSP, and source maps are verified.
Primary documentation
Prettier CLI documents check/write behavior and file matching.
HTML-validate documents configuration, rules, integrations, and framework handling.
Stylelint user guide covers configuration, CLI, custom syntaxes, and fixes.
jq manual documents strict JSON parsing and transformations.
Python json.tool documents standard-library validation/pretty-printing.
JSON Schema 2020-12 defines the schema dialect used in the example.
Ajv guide explains compilation and JSON Schema validation.
cssnano documents PostCSS-based CSS optimization and presets.
Comments and corrections