Perfil de Usuario ERP con JavaFX y Tailwind CSS
Construye un panel de perfil de usuario moderno estilo ERP empresarial usando JavaFX, FXML y la librería FxThemeBridge con clases Tailwind. Incluye métricas en tiempo real, tabs de actividad y sistema de permisos.
JJ Arroyo
24 de marzo de 2026 • 15 min de lectura

En este tutorial vamos a construir desde cero un Panel de Perfil de Usuario tipo ERP empresarial usando JavaFX con FXML y la librería FxThemeBridge en modo Tailwind. El resultado es una interfaz profesional con tarjeta de empleado, métricas de rendimiento operativo, tabla de actividad y panel de permisos.
Descargar Proyecto Completo
¿Qué vamos a construir?
Una interfaz dividida en dos paneles:
- Panel izquierdo: Tarjeta del empleado con avatar, datos corporativos (ID, departamento, ubicación, turno) y contacto.
- Panel derecho: Sistema de tabs con tres secciones:
- Rendimiento Operativo: Métricas en vivo (tasa de recolección, precisión, tareas, incidentes) con barra de progreso.
- Registro de Actividad: Tabla con el historial de acciones del turno actual.
- Accesos y Permisos: Badges de módulos autorizados y datos de seguridad.
Requisitos
- Java 17+ y JavaFX SDK 21+
- Librería
fx-themebridge-library.jaren la carpetalib/
Estructura del Proyecto
userprofile/
├── run.bat
├── lib/
│ └── fx-themebridge-library.jar
├── src/main/java/com/userprofile/
│ ├── App.java
│ ├── controller/
│ │ └── MainController.java
│ └── model/
│ └── Employee.java
└── src/main/resources/
├── css/
│ └── style.css
└── fxml/
└── MainView.fxml
Paso 1: Configurar FxThemeBridge con Tailwind
Lo primero es copiar fx-themebridge-library.jar a la carpeta lib/ del proyecto. Luego, en App.java, inicializamos el tema Tailwind antes de cargar nuestro CSS custom:
package com.userprofile;
import com.fxthemebridge.FxThemeBridge;
import javafx.application.Application;
import javafx.fxml.FXMLLoader;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.stage.Stage;
public class App extends Application {
@Override
public void start(Stage primaryStage) throws Exception {
FXMLLoader loader = new FXMLLoader(getClass().getResource("/fxml/MainView.fxml"));
Parent root = loader.load();
Scene scene = new Scene(root, 1100, 650);
// Cargar Tailwind desde FxThemeBridge
FxThemeBridge.init(scene, FxThemeBridge.Target.TAILWIND);
// CSS custom sobre la base Tailwind
scene.getStylesheets().add(getClass().getResource("/css/style.css").toExternalForm());
primaryStage.setTitle("Perfil de Usuario - ERP Global Systems v4.2");
primaryStage.setScene(scene);
primaryStage.show();
}
public static void main(String[] args) {
launch(args);
}
}
Con una sola línea, FxThemeBridge.init(scene, FxThemeBridge.Target.TAILWIND), obtenemos acceso a cientos de clases utilitarias tipo Tailwind: bg-gray-100, text-blue-600, font-bold, p-6, card, badge, btn, y muchas más.
Paso 2: El Modelo — Employee
Usamos JavaFX Properties para que nuestro modelo sea reactivo y listo para binding:
package com.userprofile.model;
import javafx.beans.property.*;
public class Employee {
private final StringProperty id = new SimpleStringProperty();
private final StringProperty name = new SimpleStringProperty();
private final StringProperty role = new SimpleStringProperty();
private final StringProperty department = new SimpleStringProperty();
private final StringProperty location = new SimpleStringProperty();
private final StringProperty shift = new SimpleStringProperty();
private final StringProperty email = new SimpleStringProperty();
private final StringProperty phone = new SimpleStringProperty();
private final BooleanProperty active = new SimpleBooleanProperty(true);
private final IntegerProperty collectionRate = new SimpleIntegerProperty();
private final DoubleProperty precision = new SimpleDoubleProperty();
private final IntegerProperty tasksCompleted = new SimpleIntegerProperty();
private final IntegerProperty tasksTotal = new SimpleIntegerProperty();
private final IntegerProperty incidents = new SimpleIntegerProperty();
public Employee(String id, String name, String role, String department,
String location, String shift, String email, String phone) {
this.id.set(id);
this.name.set(name);
this.role.set(role);
this.department.set(department);
this.location.set(location);
this.shift.set(shift);
this.email.set(email);
this.phone.set(phone);
}
// Property accessors
public StringProperty idProperty() { return id; }
public StringProperty nameProperty() { return name; }
public StringProperty roleProperty() { return role; }
// ... getters, setters y demás properties
}
Paso 3: El Layout FXML — Dos Paneles con Tailwind
El archivo FXML utiliza directamente las clases de FxThemeBridge Tailwind. Nada de CSS inline, todo con clases utilitarias:
<HBox styleClass="bg-gray-100" xmlns:fx="http://javafx.com/fxml/1"
fx:controller="com.userprofile.controller.MainController"
prefWidth="1100" prefHeight="650" spacing="0">
<padding>
<Insets top="24" right="24" bottom="24" left="24"/>
</padding>
<!-- Panel izquierdo: Tarjeta del empleado -->
<VBox styleClass="card-elevated" spacing="0" prefWidth="300"
alignment="TOP_CENTER">
<!-- Avatar con iniciales -->
<StackPane prefWidth="100" prefHeight="100">
<Region styleClass="avatar-circle"/>
<Label fx:id="avatarInitials" text="CM" styleClass="avatar-text"/>
<Region styleClass="online-dot" StackPane.alignment="BOTTOM_RIGHT"/>
</StackPane>
<Label fx:id="nameLabel" text="Carlos Mendoza" styleClass="h4"/>
<Label fx:id="roleLabel" text="Supervisor" styleClass="text-blue-600, text-sm"/>
<!-- Badge activo -->
<HBox styleClass="badge-active">
<Label text="●" styleClass="text-green-500"/>
<Label text="Activo" styleClass="text-green-700, font-medium"/>
</HBox>
<!-- Info corporativa con clases Tailwind -->
<Label text="INFORMACIÓN CORPORATIVA" styleClass="section-header"/>
<Label fx:id="departmentLabel" styleClass="text-primary, text-sm, font-semibold"/>
<!-- ... más campos -->
</VBox>
<!-- Panel derecho: Tabs -->
<VBox HBox.hgrow="ALWAYS">
<TabPane fx:id="tabPane" styleClass="tab-pane">
<Tab text="Rendimiento Operativo" closable="false">
<!-- Métricas en cards -->
</Tab>
<Tab text="Registro de Actividad" closable="false">
<!-- Tabla de actividad -->
</Tab>
<Tab text="Accesos y Permisos" closable="false">
<!-- Badges y seguridad -->
</Tab>
</TabPane>
</VBox>
</HBox>
Clases Tailwind usadas en el FXML
| Clase | Función |
|---|---|
bg-gray-100 | Fondo gris claro del root |
card-elevated | Tarjeta con sombra elevada |
text-blue-600 | Texto azul para el rol |
text-muted | Texto gris secundario |
text-green-500 | Indicador verde |
font-semibold | Peso semibold |
h4 | Heading nivel 4 |
badge, badge-primary | Badges de permisos |
card-colored-green | Card con fondo verde para el mensaje de objetivo |
btn, btn-outline | Botón con borde |
Paso 4: Las Métricas — Cards con Datos en Vivo
Cada métrica vive en su propia card con el valor numérico grande y una etiqueta descriptiva:
<VBox styleClass="card, metric-card" HBox.hgrow="ALWAYS" alignment="CENTER">
<HBox spacing="6" alignment="CENTER">
<Label text="📦"/>
<Label text="Tasa de Recolección" styleClass="text-muted, text-xs"/>
</HBox>
<Label fx:id="collectionRateLabel" text="185" styleClass="metric-value"/>
<Label text="und/hr" styleClass="text-muted, text-sm"/>
</VBox>
La barra de progreso de tareas se estiliza con la clase progress-primary definida en nuestro CSS custom:
.progress-primary .bar {
-fx-background-color: #3b82f6;
-fx-background-radius: 4px;
}
.progress-primary .track {
-fx-background-color: #e5e7eb;
-fx-background-radius: 4px;
}
Paso 5: El Controlador — Binding y Tabla
El controlador crea el Employee con datos demo y los enlaza con los Labels del FXML. La tabla de actividad usa TableView<String[]> para mantener el ejemplo simple:
@FXML
public void initialize() {
employee = new Employee(
"EMP-8472", "Carlos Mendoza", "Supervisor de Almacén",
"Logística y Distribución", "Bodega Central - Zona A",
"Turno Mañana (06:00 - 14:00)",
"carlos.mendoza@empresa.com", "+57 300 123 4567"
);
employee.setCollectionRate(185);
employee.setPrecision(99.8);
employee.setTasksCompleted(145);
employee.setTasksTotal(150);
employee.setIncidents(1);
bindEmployeeData();
setupActivityTable();
}
private void setupActivityTable() {
timeColumn.setCellValueFactory(
data -> new SimpleStringProperty(data.getValue()[0])
);
actionColumn.setCellValueFactory(
data -> new SimpleStringProperty(data.getValue()[1])
);
// ... zona y estado
activityTable.setItems(FXCollections.observableArrayList(
new String[]{"06:05", "Inicio de turno", "Acceso Principal", "Completado"},
new String[]{"07:30", "Despacho #ORD-4521", "Zona Despacho", "Completado"},
new String[]{"09:20", "Incidente - Caja dañada", "Zona A", "Reportado"}
));
}
Paso 6: CSS Custom — Solo lo que Tailwind no cubre
La magia de FxThemeBridge es que nuestro style.css es mínimo. Solo definimos lo específico de esta app:
/* Avatar circular con gradiente */
.avatar-circle {
-fx-background-color: linear-gradient(to bottom right, #e0e7ff, #c7d2fe);
-fx-background-radius: 9999px;
-fx-pref-width: 100px;
-fx-pref-height: 100px;
}
/* Indicador verde de "en línea" */
.online-dot {
-fx-background-color: #22c55e;
-fx-background-radius: 9999px;
-fx-border-color: white;
-fx-border-width: 3px;
}
/* Valor grande de las métricas */
.metric-value {
-fx-font-size: 32px;
-fx-font-weight: bold;
-fx-text-fill: #111827;
}
/* Encabezado de sección */
.section-header {
-fx-font-size: 11px;
-fx-font-weight: bold;
-fx-text-fill: #6366f1;
}
Todo lo demás — colores de texto, backgrounds, bordes, paddings, badges, cards, botones, tabs, tablas — viene gratis de FxThemeBridge Tailwind.
Resultado Final
La aplicación muestra una interfaz idéntica a un dashboard web moderno:
- Tarjeta lateral con avatar, estado activo, datos corporativos y contacto
- Métricas operativas con cards individuales y barra de progreso
- Tabla de actividad con el historial del turno completo
- Panel de permisos con badges de colores por módulo
Puntos Clave
- FxThemeBridge simplifica todo: Con
FxThemeBridge.init(scene, "tailwind")obtienes un sistema de diseño completo. - FXML + Tailwind = Productivo: Las clases utilitarias directamente en
styleClassdel FXML eliminan la necesidad de escribir CSS extenso. - CSS custom mínimo: Solo ~100 líneas para los componentes específicos del perfil.
- Datos reactivos: El modelo usa
Propertyde JavaFX, listo para binding bidireccional si necesitas editabilidad.
Requisitos: Java 17+, JavaFX 21+, tener configurada la variable
JAVAFX_PATHenrun.bat.