web php
HTML, CSS, And JavaScript Integration
PHP often renders the first HTML response. CSS controls presentation. JavaScript adds client-side behaviour. A PHP developer does not need to be a frontend specialist to work safely here, but they do need to understand how server-rendered data crosses into HTML, CSS, and JavaScript.
The key rule is context: a value printed into HTML must be escaped for HTML, and a value passed into JavaScript should be JSON encoded.
Render HTML and pass small JS data safely
<?php
declare(strict_types=1);
$pageTitle = 'Course progress';
$progress = ['completed' => 7, 'total' => 12];
$title = htmlspecialchars($pageTitle, ENT_QUOTES | ENT_SUBSTITUTE, 'UTF-8');
$progressJson = json_encode(
$progress,
JSON_THROW_ON_ERROR | JSON_HEX_TAG | JSON_HEX_APOS | JSON_HEX_AMP | JSON_HEX_QUOT
);
echo "<h1>{$title}</h1>" . PHP_EOL;
echo "<script>const progress = {$progressJson};</script>" . PHP_EOL;
// Prints:
// <h1>Course progress</h1>
// <script>const progress = {"completed":7,"total":12};</script>
Do not build JavaScript strings by manually concatenating quotes and user data. JSON encoding knows how to represent strings, numbers, booleans, arrays, and objects safely for JavaScript.
Link CSS and JavaScript deliberately
A server-rendered page can link static CSS and JavaScript assets.
<?php
declare(strict_types=1);
$assetVersion = '2026-05-28';
echo '<link rel="stylesheet" href="/assets/app.css?v=' . rawurlencode($assetVersion) . '">' . PHP_EOL;
echo '<script src="/assets/app.js?v=' . rawurlencode($assetVersion) . '" defer></script>' . PHP_EOL;
// Prints:
// <link rel="stylesheet" href="/assets/app.css?v=2026-05-28">
// <script src="/assets/app.js?v=2026-05-28" defer></script>
defer lets the browser download the script without blocking HTML parsing, then run it after the document has been parsed. It is a good default for many server-rendered pages.
The query string version is a simple cache-busting technique. Larger projects often use a build tool that creates hashed filenames instead.
Data attributes are useful for small values
For small pieces of data tied to an element, data-* attributes are often simpler than a script block.
<?php
declare(strict_types=1);
function escapeHtml(string $value): string
{
return htmlspecialchars($value, ENT_QUOTES | ENT_SUBSTITUTE, 'UTF-8');
}
$userId = '42';
$displayName = 'Asha <Admin>';
echo '<button data-user-id="' . escapeHtml($userId) . '">'
. escapeHtml($displayName)
. '</button>' . PHP_EOL;
// Prints:
// <button data-user-id="42">Asha &lt;Admin&gt;</button>
JavaScript can read element.dataset.userId. Keep data attributes small and simple. Do not dump large application state into HTML attributes.
Progressive enhancement
Progressive enhancement means the server-rendered page still does the important job without JavaScript, while JavaScript improves the experience when available.
A delete button might be a normal POST form first. JavaScript can later add a confirmation modal or AJAX request.
<?php
declare(strict_types=1);
$id = 42;
echo '<form method="post" action="/tasks/' . $id . '/delete">'
. '<button type="submit" data-confirm="Delete this task?">Delete</button>'
. '</form>' . PHP_EOL;
// Prints:
// <form method="post" action="/tasks/42/delete"><button type="submit" data-confirm="Delete this task?">Delete</button></form>
The server route must still validate authorization and CSRF tokens. JavaScript is not a security boundary.
Keep CSS concerns in CSS
PHP should choose semantic classes and state attributes, not generate lots of inline styles.
<?php
declare(strict_types=1);
$status = 'overdue';
$allowedStatuses = ['paid', 'pending', 'overdue'];
$class = in_array($status, $allowedStatuses, true) ? 'invoice--' . $status : 'invoice--pending';
echo '<span class="invoice ' . $class . '">Overdue</span>' . PHP_EOL;
// Prints:
// <span class="invoice invoice--overdue">Overdue</span>
The CSS file can decide what .invoice--overdue looks like.
What to check in a project
Check that server values printed into HTML are escaped with the right helper or template engine syntax.
Check that data passed to JavaScript uses JSON encoding, not hand-built JavaScript.
Check that linked scripts use defer or an intentional loading strategy.
Check that JavaScript enhancements do not replace server-side validation, authorization, or CSRF protection.
Check that PHP is not generating unnecessary inline CSS when a class or data attribute would be cleaner.
What you should be able to do
After this lesson, you should be able to render safe HTML, link CSS and JavaScript assets, pass PHP data into JavaScript safely, use data attributes for small values, and explain why JavaScript enhances but does not secure a PHP application.
Practice
Task: Render A Page Shell
Write a small PHP script that renders a page shell with HTML, linked assets, and safe JavaScript data.
Requirements
- Use
declare(strict_types=1);. - Escape the page title for HTML.
- Link a CSS file.
- Link a JavaScript file with
defer. - Pass a small PHP array into JavaScript using
json_encode(). - Include one value that contains characters like
<or&to prove escaping. - Print the rendered output.
Check Your Work
Run the script and confirm that HTML text is escaped while the JavaScript data is valid JSON.
Show solution
This solution uses different output strategies for HTML and JavaScript.
<?php
declare(strict_types=1);
function escapeHtml(string $value): string
{
return htmlspecialchars($value, ENT_QUOTES | ENT_SUBSTITUTE, 'UTF-8');
}
$title = 'Course <Progress>';
$assetVersion = '2026-05-28';
$progress = ['completed' => 7, 'total' => 12, 'label' => 'PHP & web'];
$progressJson = json_encode(
$progress,
JSON_THROW_ON_ERROR | JSON_HEX_TAG | JSON_HEX_APOS | JSON_HEX_AMP | JSON_HEX_QUOT
);
echo '<!doctype html>' . PHP_EOL;
echo '<title>' . escapeHtml($title) . '</title>' . PHP_EOL;
echo '<link rel="stylesheet" href="/assets/app.css?v=' . rawurlencode($assetVersion) . '">' . PHP_EOL;
echo '<script src="/assets/app.js?v=' . rawurlencode($assetVersion) . '" defer></script>' . PHP_EOL;
echo '<h1>' . escapeHtml($title) . '</h1>' . PHP_EOL;
echo '<script>window.progress = ' . $progressJson . ';</script>' . PHP_EOL;
// Prints:
// <!doctype html>
// <title>Course &lt;Progress&gt;</title>
// <link rel="stylesheet" href="/assets/app.css?v=2026-05-28">
// <script src="/assets/app.js?v=2026-05-28" defer></script>
// <h1>Course &lt;Progress&gt;</h1>
// <script>window.progress = {"completed":7,"total":12,"label":"PHP \u0026 web"};</script>
The title is escaped for HTML because it is rendered into <title> and <h1>. The progress data is JSON encoded because it is consumed by JavaScript.
Why This Works
The example uses the right escaping for each context. It also links CSS and JavaScript as assets instead of mixing all behaviour into the PHP file.