Proyecto Web — PHP + Vite + Tailwind + CI/CD
Esta documentación describe un proyecto web construido con
PHP clásico (archivos .php planos),
usando Vite como bundler para JavaScript y CSS,
Tailwind CSS 3 como framework de estilos y un pipeline
CI/CD en GitHub Actions que compila el front-end y
despliega automáticamente los archivos a un hosting compartido vía FTP.
El objetivo es tener una landing / sitio informativo con PHP,
pero con tooling moderno (Vite + Tailwind) y un flujo de despliegue profesional:
cada vez que se hace git push main, el sitio se reconstruye y se actualiza
en el servidor sin subir archivos manualmente.
Objetivos del sistema
Los objetivos principales del proyecto son:
- Seguir usando PHP simple (sin frameworks grandes) para las vistas y la lógica básica, pero con un front-end moderno.
- Integrar Tailwind CSS 3 en modo compilado, con purga automática de clases leyendo directamente el HTML y las vistas PHP.
-
Aprovechar Vite para:
- Compilar y minificar CSS y JS.
- Generar assets con hash para cache busting
(por ejemplo
main-XXXXXX.js). - Usar
npm run devcon recarga rápida en local.
-
Conectar Vite con PHP mediante un loader que lea el
manifest.jsony genere correctamente las etiquetas<link>y<script>en producción. -
Configurar un pipeline CI/CD que:
- Se ejecute automáticamente al hacer push a la rama
main. - Efectúe la instalación de dependencias y el build de Vite.
- Suba solo los archivos compilados (dist) y PHP necesarios vía FTP a un hosting compartido (por ejemplo Hostinger).
- Se ejecute automáticamente al hacer push a la rama
- Mejorar el rendimiento y PageSpeed, evitando CSS y fuentes bloqueantes de terceros (como Google Fonts) y sirviéndolo todo desde el propio servidor.
Tecnologías y herramientas utilizadas
- PHP 8+ — motor principal del sitio y lógica de las vistas.
- Vite — bundler para JS/CSS, encargado de generar la carpeta
dist/con assets minificados y elmanifest.json. - Tailwind CSS 3 — framework CSS utility-first, configurado con
purga de clases en función de archivos
.phpysrc/. - Node.js 20.19.x — versión utilizada en el runner de GitHub Actions para ejecutar Vite y Tailwind.
- GitHub + GitHub Actions — repositorio remoto y orquestador del pipeline CI/CD.
- SamKirkland/FTP-Deploy-Action — acción de GitHub para el deploy por FTP/FTPS.
- Hosting compartido (ej. Hostinger) —
servidor donde se suben los archivos finales a
/public_html. - Fuentes locales (Figtree) — tipografía servida desde el propio servidor para evitar bloqueo de render por Google Fonts.
Arquitectura de carpetas y estructura general
A nivel de desarrollo local (por ejemplo dentro de htdocs), la estructura del
proyecto se organiza aproximadamente así:
proyecto-web-php-vite-tailwind/
├── index.php
├── contacto.php
├── nosotros.php
├── vite.loader.php
├── src/
│ ├── main.js
│ ├── input.css // Tailwind + estilos base
│ └── fonts.css // Definición de fuente local (Figtree)
├── public/
│ └── fonts/
│ └── Figtree-VariableFont_wght.ttf
├── package.json
├── package-lock.json
├── vite.config.js
├── tailwind.config.js
├── postcss.config.js
└── .github/
└── workflows/
└── deploy.yml // Pipeline CI/CD de GitHub Actions
Tras ejecutar npm run build en local o en el servidor de GitHub Actions,
Vite genera la carpeta dist/, y en el hosting la estructura final queda
aproximadamente así (el contenido de dist/ se sube al
/public_html):
public_html/
├── index.php
├── contacto.php
├── nosotros.php
├── vite.loader.php
├── .vite/
│ └── manifest.json // Mapa de assets generado por Vite
└── assets/
├── main-XXXXXX.css
├── main-XXXXXX.js
└── ...otros assets (imágenes, fuentes procesadas, etc.)
Integración de Vite + Tailwind en un proyecto PHP
Aunque Vite suele usarse con React, Vue o similares, aquí se utiliza como herramienta de build para una web en PHP “clásico”: se encarga de compilar el CSS de Tailwind, empaquetar el JS y generar el manifest de assets.
Punto de entrada: src/main.js
// src/main.js
import './input.css';
import './fonts.css';
console.log('Vite + Tailwind + PHP funcionando');
Hoja principal de estilos: src/input.css
@tailwind base;
@tailwind components;
@tailwind utilities;
body {
@apply font-sans antialiased;
}
Fuente local: src/fonts.css
En lugar de depender de Google Fonts, se usa una fuente local (por ejemplo Figtree) para evitar peticiones bloqueantes externas:
/* src/fonts.css */
@font-face {
font-family: "Figtree";
src: url("/fonts/Figtree-VariableFont_wght.ttf") format("truetype");
font-weight: 100 900;
font-display: swap;
}
Configuración de Vite para build y manifest
Vite se configura para generar una carpeta dist/ con un
manifest.json y rutas relativas para los assets:
// vite.config.js
import { defineConfig } from 'vite';
export default defineConfig({
build: {
outDir: 'dist',
manifest: true, // Genera dist/.vite/manifest.json
rollupOptions: {
input: 'src/main.js', // Punto de entrada principal
},
emptyOutDir: true,
},
});
El manifest resultante suele tener esta forma:
{
"src/main.js": {
"file": "assets/main-DBIKJFer.js",
"src": "src/main.js",
"isEntry": true,
"css": [
"assets/main-BUXrEYNY.css"
]
}
}
Nótese que Vite guarda rutas relativas (por ejemplo "assets/main-..."),
así que el código PHP debe respetarlas tal cual y no concatenar /assets/ dos veces.
Tailwind CSS 3 y purga de estilos en un sitio PHP
Tailwind 3 se configura para analizar tanto los archivos de src/ como las vistas
.php, de forma que solo genere las utilidades realmente usadas.
// tailwind.config.js
export default {
content: [
"./*.php",
"./**/*.php",
"./src/**/*.{js,ts}"
],
theme: {
extend: {
fontFamily: {
sans: ['Figtree', 'sans-serif'],
},
},
},
plugins: [],
};
Esto garantiza que el CSS final sea muy compacto y solo contenga las utilidades necesarias para las clases Tailwind que aparecen en los archivos PHP y JS. Esta purga automática es una de las razones por las que tiene sentido usar CI/CD: cada vez que cambian las vistas, el CSS debe reconstruirse correctamente en producción.
Loader PHP ↔ Vite: uso de manifest.json
Para conectar el build de Vite con las vistas PHP, se crea un archivo
vite.loader.php que:
- En producción, lee
.vite/manifest.jsony construye las rutas a CSS/JS. - En desarrollo, utiliza directamente el Vite Dev Server (
http://localhost:5173).
<?php
function loadViteAssets(string $entry = "src/main.js") {
// Manifest generado por Vite en producción
$manifestPath = __DIR__ . "/.vite/manifest.json";
// 🚀 PRODUCCIÓN
if (file_exists($manifestPath)) {
$manifest = json_decode(file_get_contents($manifestPath), true);
if (!isset($manifest[$entry])) {
return "<!-- Entry $entry no encontrado en manifest -->";
}
// El manifest ya trae rutas tipo "assets/main-XXXX.css"
$css = $manifest[$entry]["css"][0] ?? null;
$js = $manifest[$entry]["file"];
$html = "";
// CSS
if ($css) {
$html .= '<link rel="stylesheet" href="/' . $css . '">' . PHP_EOL;
}
// JS
$html .= '<script type="module" src="/' . $js . '"></script>' . PHP_EOL;
return $html;
}
// 🧑💻 DESARROLLO: Vite Dev Server
return <<<HTML
<!-- VITE DEV -->
<script type="module" src="http://localhost:5173/src/main.js"></script>
HTML;
}
En cada archivo PHP (por ejemplo index.php) el loader se utiliza así:
<?php include __DIR__ . "/vite.loader.php"; ?>
<!DOCTYPE html>
<html lang="es">
<head>
<meta charset="UTF-8">
<title>Inicio</title>
<?= loadViteAssets("src/main.js"); ?>
</head>
Pipeline CI/CD con GitHub Actions y FTP
El pipeline CI/CD se define en .github/workflows/deploy.yml y se ejecuta
cada vez que se hace push a la rama main. El flujo básico es:
- Descargar el repositorio.
- Instalar Node.js (versión compatible con Vite).
- Instalar dependencias con
npm ci. - Ejecutar
npm run buildpara generardist/. - Subir el contenido de
dist/vía FTP al/public_htmldel hosting.
# .github/workflows/deploy.yml
name: Deploy to Shared Hosting via FTP
on:
push:
branches:
- main
jobs:
ftp-deploy:
name: Build & Upload Site
runs-on: ubuntu-latest
steps:
- name: Checkout repository
uses: actions/checkout@v4
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: 20.19.0
- name: Install dependencies
run: npm ci
- name: Build project
run: npm run build
- name: FTP Deploy (only dist/)
uses: SamKirkland/FTP-Deploy-Action@v3.1.1
with:
ftp-server: ${{ secrets.FTP_HOST }}
ftp-username: ${{ secrets.FTP_USER }}
ftp-password: ${{ secrets.FTP_PASSWORD }}
local-dir: dist/
server-dir: /public_html/
Los valores de FTP_HOST, FTP_USER y FTP_PASSWORD se
almacenan como secrets en el repositorio (Settings → Actions → Secrets).
Problemas encontrados y soluciones aplicadas
Durante la configuración y puesta en marcha del pipeline, se presentaron varios problemas que se fueron resolviendo:
-
Action de FTP con versión inexistente:
Se intentó usarSamKirkland/FTP-Deploy-Action@v4, que no estaba disponible, causando errores del tipo “unable to find version”.
Solución: usar la versión estableSamKirkland/FTP-Deploy-Action@v3.1.1. -
Node.js incompatible con Vite:
Algunas versiones de Node en el runner no eran compatibles con Vite, que requiere Node 18+ o 20+.
Solución: fijar explícitamentenode-version: 20.19.0enactions/setup-node. -
Inputs incorrectos para la acción FTP:
En la versión 3 de la acción, los parámetros correctos sonftp-server,ftp-usernameyftp-password, noserverousername.
Solución: ajustar los nombres de los campos en eldeploy.yml. -
Desalineación entre
dist/ypublic_html:
El contenido dedist/se sube directamente a/public_html, por lo que en el servidor ya no existe la carpetadist. Inicialmente, el loader buscaba/dist/.vite/manifest.json.
Solución: cambiar la ruta del manifest envite.loader.phpa:__DIR__ . "/.vite/manifest.json". -
Rutas duplicadas
/assets/assets/...:
El manifest ya incluía"assets/main-...", pero el loader concatenaba/assets/al inicio, generando rutas como/assets/assets/main-XXXX.css.
Solución: usar directamente el campo del manifest y solo anteponer una barra inicial:"/" . $css,"/" . $js. -
Penalización por Google Fonts en PageSpeed:
El uso de@importde Google Fonts generaba una petición extra que bloqueaba el renderizado inicial.
Solución: descargar la fuente (por ejemplo Figtree), servirla localmente y definirla con@font-faceenfonts.css, eliminando por completo la dependencia de Google Fonts en el sitio productivo.
Rendimiento y mejoras de PageSpeed
Tras aplicar la integración de Vite + Tailwind con purga, hosting de fuente local y el pipeline CI/CD, los resultados típicos en móviles son:
- Rendimiento móvil cercano a 100/100 en Lighthouse.
- FCP (First Contentful Paint) alrededor de ~1s.
- LCP (Largest Contentful Paint) estable en ~1s.
- TBT (Total Blocking Time) prácticamente 0 ms.
- CLS (Cumulative Layout Shift) ≈ 0, gracias a definir tamaños de imagen y layout estable.
Aunque Lighthouse puede seguir mostrando alguna advertencia sobre CSS que “bloquea render”,
esto es normal: cualquier hoja de estilos en el <head> genera
cierto bloqueo mínimo. Lo importante es que el impacto sea muy bajo (pocos milisegundos)
y que los recursos se sirvan de forma eficiente desde el propio servidor.
Ventajas del enfoque PHP + Vite + Tailwind + CI/CD
- Mantener PHP simple: se conserva un stack muy fácil de desplegar en cualquier hosting compartido, sin necesidad de Node en el servidor.
- Front-end moderno: se aprovechan Vite y Tailwind para tener CSS/JS optimizados, recarga rápida en desarrollo y assets agrupados por hash para cache busting.
-
Purga automática de CSS:
Tailwind analiza los archivos
.phpysrc/, generando solo las clases necesarias, lo que reduce el tamaño del CSS final. -
Deploy profesional:
con el pipeline CI/CD, cada push a
maindesencadena: build → deploy → sitio actualizado, sin FTP manual. -
Menos errores humanos:
se evita olvidar subir el
manifest.json, el CSS actualizado, o mezclar builds viejos con código nuevo. - Buen rendimiento en mobile: fuentes locales, assets minificados y HTML/JS livianos se traducen en buenas métricas de Lighthouse y mejor UX.
- Base escalable: es fácil añadir nuevas páginas PHP, más JS o más estilos, manteniendo la misma arquitectura y el mismo pipeline de despliegue.
Resultados y aprendizajes
Este proyecto demuestra que es totalmente viable:
- Combinar PHP clásico con un stack moderno de front-end (Vite + Tailwind) sin migrar a frameworks complejos.
- Usar CI/CD con GitHub Actions incluso en hostings compartidos basados en FTP.
-
Conseguir métricas de rendimiento altas (especialmente en mobile)
mediante:
- Purga de CSS de Tailwind.
- Fuentes locales con
font-display: swap. - Imágenes optimizadas y tamaños definidos.
-
Organizar un flujo de trabajo cómodo:
desarrollo en local con
npm run dev+ confirmación visual +git push main→ deploy automático. - Aprender a interpretar y corregir problemas típicos de pipelines: versiones de Node, versiones de actions, rutas de assets, manifest, etc.