openxava / documentación / Estilo visual personalizado

Tabla de contenidos

Estilo visual personalizado
Definir tu propio estilo
Extender un estilo existente (nuevo en v6.3)
Tu propio esquema de colores (nuevo en v6.3)
Refinar otros aspectos de la interfaz de usuario
Modificar el estilo de la aplicación (nuevo en v4.5)
Añadir elementos a la página (nuevo en v5.6)
Selector de tema (nuevo en v6.3)
Dentro de portales
Definir tu propio estilo (hasta v5.9.1)
Definir tu propio estilo (hasta v4.9.1)
Estilos predefinidos (hasta v4.9.1)
Un ejemplo: estilo para iPad (hasta v4.9.1)
Detección de navegador/dispositivo
Código JavaScript
Archivo CSS
Clase de estilo

OpenXava incluye varios estilos visuales. Por defecto usa Terra:
Terra theme sample


También puedes usar el tema Light:
Light theme sample

Y Dark (nuevo en v6.3):


O Blank & White (nuevo en v6.3):

También Blue (nuevo en v6.3):

Para escoger uno edita el archivo xava.properties de la carpeta properties de tu proyecto y pon valor a la entrada styleCSS:
# Visual style
# styleCSS=terra.css
# styleCSS=light.css
styleCSS=dark.css
# styleCSS=black-and-white.css
# styleCSS=blue.css

Definir tu propio estilo

Para crear un nuevo estilo crea tu propio archivo CSS en la carpeta src/main/webapp/xava/style (web/xava/style antes de v7.0) de tu proyecto y apunta a él en la propiedad styleCSS de xava.properties.

Extender un estilo existente (nuevo en v6.3)

Si simplemente quieres una pequeña modificación de un estilo existente, puedes extender ese estilo. Mira los CSSs para los estilos disponibles en GitHub. Por ejemplo, si quieres el estilo Light pero con el menú de la izquierda y el botón principal dorados, crea un archivo dorado.css en src/main/webapp/xava/style con este contenido:

/* dorado.css */

@import 'light.css';

:root {

	--mi-oro: #cda434;
	--my-highlight: var(--mi-oro);
	
	/* Menú de módulos de la izquierda */	
	--modules-list-color: var(--my-lightgray); 
	--modules-list-background: var(--mi-oro);

}
Fíjate que usamos @import 'light.css' para extender el estilo Light. Redefinimos la variable --my-highlight que es el color para el botón principal, esta variable es de light.css. Después definimos los colores para el menú de la izquierda. Ahora cambio el valor de styleCSS en xava.properties:
styleCSS=dorado.css
Para obtener este resultado:
Por otra parte puedes definir tu propio esquema de colores, no sólo un refinamiento, extendiendo directamente de base.css. Por ejemplo, para definir un estilo Rosa crea un rosa.css como este:
/* rosa.css */

@import 'base.css';

:root {
	--mi-rosa: #E94E77;
	--mi-rosa-oscuro: #D68189;
	--mi-beis: #F4EAD5;
	--mi-marron: #C6A49A;
	--rosa-transparente: rgb(198, 164, 154, 0.2); 
	
	 
	/* Menú de módulos de la izquierda */
	--modules-list-color: var(--mi-beis);
	--modules-list-background: var(--mi-rosa);
	--modules-list-selected-color: white;
	 
	/* Cabecera del módulo */ 
	--module-header-color: white;
	--module-header-background: var(--mi-rosa);		
	
	/* Botonera */	 
	--button-bar-button-color: var(--mi-beis);
	--button-bar-background: var(--mi-rosa-oscuro);
	--list-formats-color: var(--mi-beis);
	--list-formats-hover-color: white;
	
	/* Fondos y etiquetas */
	--background: var(--mi-beis);
	--color: var(--mi-marron);
	
	/* Marcos */	
	--frame-background: var(--rosa-transparente);
	
	/* Acciones */
	--action-color: var(--mi-rosa-oscuro);	
	--action-hover-color: var(--mi-rosa);
	--action-hover-background: white;
	--default-action-button-background: var(--mi-rosa);
	
	/* Editores */
	--required-editor-border: var(--mi-rosa-oscuro);
	
}
Fíjate como extendemos de base.css y definimos los colores para las partes principales de la interfaz. Con sólo el código de arriba obtienes:
Sólo tienes que definir unos pocos colores y todos los demás se derivan a partir de ellos, así tienes un esquema de colores consistente con poco esfuerzo. Además, tienes la opción de definir individualmente el color para que detalle de la interfaz de usuario, echa un vistazo a base.css para ver todas las variables de color disponibles, más de 200.
Pero no sólo los colores, puedes refinar cualquier aspecto de la interfaz: márgenes, fuentes, bordes, etc. Por ejemplo, si quiere cambiar el bonito degradado que se usa por defecto para los marcos, por un más convencional y aburrido marco con sombra, puedes añadir estas líneas a tu CSS:
.ox-frame {
	background: white;
	box-shadow: 0px 0px 4px gray;
}
De esta manera, los marcos se visualizarán así:
Puedes modificar cualquier cosa de la interfaz gráfica, echa un vistazo a base.css (o terra.css/light.css antes v6.3) para ver todas las acciones disponibles. Por supuesto, si vas a cambiar muchas cosas una opción es definir tu propio CSS desde cero, sin extender de base.css.

Modificar el estilo de la aplicación (nuevo en v4.5)

Desde la versión 4.5 podemos añadir y modificar estilos de la aplicación mediante el fichero custom.css en src/main/webapp/xava/style (web/xava/style antes de v7.0). Esto afecta sólo a tu aplicación. Esto es una forma fácil de cambiar el estilo sin tener que crear un nuevo tema.
Para sobreescribir un estilo ya existente, basta con añadir el nombre del estilo y definirlo en nuestro fichero. Poniendo:
body {
   font-size: 9px;
}
.ox-list-pair, .ox-list-odd {
   height: 20px;
}
Cambiamos el tamaño de la letra de toda nuestra aplicación (incluso dentro de un portal) y la altura de las filas en la lista. Mira en base.css (o terra.css/light.css antes v6.3) para ver todas las classes CSS disponibles.

Añadir elementos a la página (nuevo en v5.6)

Si queremos añadir nuestros propios elementos a la pagina para que aparezcan siempre visibles en nuestra aplicación podemos hacerlo en src/main/webapp/naviox/indexExt.jsp (web/naviox/indexExt.jsp ein v6 o anterior) de nuestro proyecto, debería crear la carpeta naviox y el archivo indexExt.jsp. Esto se incluye al final de la página, pero usando CSS podemos colocarlo en cualquier parte de la pantalla.
Por ejemplo, si queremos poner un cartelito de BETA en la esquina superior derecha de la pantalla, así:
beta-corner_es.png
Podemos poner en nuestro indexExt.jsp:
<div id="beta">BETA</div>
Y después posicionarlo añadiendo lo siguiente a custom.css:
#beta {
    position:absolute;
    right: -65px;
    top: 5px;
    color: white;
    font-size: 120%;
    font-weight: bold;
    transform: rotate(45deg);
    -webkit-transform: rotate(45deg);
    background: red;
    padding: 50px 50px 5px 50px;
    z-index: -10;
}

Selector de tema (nuevo en v6.3)

El usuario tiene la posibilidad de escoger el estilo para el mismo con un selector de temas abajo en la página:
Para determinar los temas disponibles has de enumerarlos en la propiedad themes de xava.properties:
themes=terra.css, light.css, dark.css, black-and-white.css, blue.css	
Si quieres quitar el selector de temas completamente define themes como vacía, o simplemente no la definas:
# Vacío para desactivar el selector de temas
themes=

Dentro de portales (hasta v6.6.3)

Dentro de un portal los portlets de OpenXava adquieren automáticamente la apariencia del portal, por lo tanto si trabajas con un portal simplemente has de crear o modificar el estilo del propio portal, y la aplicación de OpenXava se adaptará. Por ejemplo, cuando despliegas tu aplicación en Liferay se ve de esta forma:
liferay6-customer-list_es.png

Adicionalmente, si quieres un estilo específico para un portal, las siguientes propiedades están disponible en xava.properties: liferay6StyleClass, liferay51StyleClass, liferay41StyleClass, liferay43StyleClass, webSpherePortal61StyleClass, webSpherePortal6StyleClass, webSpherePortalStyleClass, JetSpeed2Style.
Así, si quieres crear tu propia estilo para las aplicaciones OpenXava cuando éstas ruedan dentro de un Liferary 6, añade la siguiente línea a xava.properties:
liferay6StyleClass=org.openxava.test.style.MyLiferay6Style
Y por supuesto, tendrás que escribir la clase MyLiferayStyle.

Definir tu propio estilo (hasta v5.9.1)

A partir de v5.0 el estilo visual por defecto está definido en /web/naviox/style/naviox.css. El archivo naviox.css está en tu proyecto pero se copia desde el proyecto Addons al desplegar. En todo caso, en lugar de modificar naviox.css es mejor modificar custom.css, como se explica en la siguiente sección.

Definir tu propio estilo (hasta v4.9.1)

Estas instrucciones son para v4.2 a v4.9.1, para versiones anteriores consultar OpenXavaTest/xava.properties. Para v5.x usa default.css como se explica arriba.
Para definir tu propio estilo añade la siguiente entrada al archivo xava.properties de tu proyecto:
styleClass=org.openxava.web.style.Style
styleCSS=mystyle/mystyle.css
Después crea una carpeta llamada mystyle dentro de la carpeta web/xava/style, y añádele un archivo llamado mystyle.css. En este archivo puedes usar las siguientes clases CSS: ox-module, ox-view, ox-detail, ox-action-link, ox-image-link, ox-button-bar, ox-button-bar-button, ox-active, ox-first, ox-last, ox-label, ox-module-description, ox-frame-content, ox-list, ox-list-header, ox-list-subheader, ox-list-pair, ox-list-odd, list-pair-selected, list-odd-selected, ox-list-info, ox-list-info-detail, ox-list-title, ox-header-list-count, ox-frame (nuevo en v4.8, antes era frame), ox-frame-actions, ox-frame-title, ox-frame-title-label, ox-errors, ox-messages, ox-section, ox-section-tab, ox-active-section, ox-section-link, ox-collection-list-actions, ox-bottom-buttons, ox-page-navigation-selected, ox-page-navigation, ox-page-navigation-pages, ox-page-navigation-arrow, ox-page-navigation-arrow-disable, ox-images-gallery (new in v4.8), ox-help, ox-total-row, ox-total-cell, ox-total-capable-cell, ox-filter-cell, ox-frame-totals, ox-frame-totals-label, ox-frame-totals-value.
Opcionalmente, puedes crear tu propia clase Style extendiendo org.openxava.web.style.Style. Esto te permite control adicional sobre la apariencia, disposición y comportamiento de tu aplicación.

Estilos predefinidos (hasta v4.9.1)

Puedes usar los estilos en esta sección con v5.0 si desactivas los menús y la identificación de usuario como se explica en la migración de v4.9.1 a v5.0.
OpenXava viene con algunos estilos predefinidos incluidos. Los puedes configurar en properties/xava.properties de tu proyecto:
# styleClass and styleCSS have only effect outside a portal,
# inside a portal OX portlets acquire automatically the portal skin.
 
# Liferay 5.1: This is the DEFAULT, so you do not need to uncomment the next lines
#styleClass=org.openxava.web.style.Liferay51Style
#styleCSS=liferay51/css/everything_unpacked.css
 
# Liferay 4.3/4.4/5.0: uncomment the next line for use this nice and slow style
#styleClass=org.openxava.web.style.Liferay43Style
#styleCSS=liferay43/css/everything_unpacked.css
 
# Classic style: uncomment the next line for use the old OX style
#styleClass=org.openxava.web.style.PortalStyle
#styleCSS=default.css

Un ejemplo: estilo para iPad (hasta v4.9.1)

Con v5.0 el estilo estándar funciona bien con tablets, de todas formas puedes usar el estilo antiguo de iPad si desactivas los menús y la identificación de usuario como se explica en la migración de v4.9.1 a v5.0. Además, a partir de v5.1 deberías añadir la entrada org.openxava.web.style.IPadStyle en styles.properties. A partir de v7.0 el estilo iPad se ha quitado completamente incluido el uso de styles.properties.
El estilo por iPad se incluye por defecto en OpenXava y se activa automáticamente en el momento en que un usuario accede a la aplicación desde un iPad.

Detección de navegador/dispositivo

Para conseguir la detección del dispositivo, añadimos el siguiente método a la clase de estilo:
public boolean isForBrowse(String browser) {
    return browser != null && browser.contains("iPad");
}
Esta es la forma para hacer corresponder un estilo específico con un navegador o dispositivo. Puedes usar esta técnica en tu estilo, aunque si lo haces, has de crear un archivo styles.properties en la carpeta properties de tu proyecto, añadiendo una lína con la clase de tu estilo, como la siguiente:
com.mycompany.myapp.style.MyDeviceStyle
Además, has de escribir la clase MyDeviceStyle con le método isForBrowse() apropiado.

Código JavaScript

El estilo para iPad también usa efectos nativos para iPad como flip (rotación) y slide (desplazamiento). Para conseguir esto usamos algo de código JavaScript. En este caso definimos nuestro propio método para especificar la forma en que un trozo de HTML obtenido del servidor vía AJAX se asigna a un elemento del documento HTML:
public String getSetHtmlFunction() {
    return "ipad.setHtml";
}
De esta forma, indicamos que la función a usar será ipad.setHtml, en vez de la estándar. Esta función está definida en el fichero ipad.js. Tenemos que indicar en la clase de estilo que este fichero se ha de cargar:
private static String [] jsFiles = {
    "ipad/ipad.js",
};
 
public String [] getNoPortalModuleJsFiles() {
    return jsFiles;
}
Este archivo ipad.js se encuentra en la carpeta web/style/ipad:
if (ipad == null) var ipad = {};
 
ipad.onLoad = function() {
    var id = $('body').data('ipad-slide-element');
    var clazz = $('body').data('ipad-slide-class');
    $('#' + id).addClass(clazz);
    setTimeout("$('#" + id + "').removeClass('" + clazz + "')", 0);
    $('body').removeData();
}
 
ipad.onClickNextPage = function(listId) {
    $('#' + listId).addClass('slided-left');
    $('body').data('ipad-slide-element', listId);
    $('body').data('ipad-slide-class', 'slided-right');
}
 
ipad.onClickPreviousPage = function(listId) {
    $('#' + listId).addClass('slided-right');
    $('body').data('ipad-slide-element', listId);
    $('body').data('ipad-slide-class', 'slided-left');
}
 
ipad.setHtml = function(id, content) {
    if (!id.match(/__core$/)) {
        $("#" + id).html(content);
        return;
    }
 
    // Core
    $("#container").addClass("container");
    $("#sheet").addClass("sheet");
    $("#front").addClass("face");
    $("#back").addClass("face");
 
    if ($("#sheet").hasClass("flipped")) {
        $("#" + id).html(content);
        $("#sheet").removeClass("flipped");
    }
    else {
        $("#core_BACK").html(content);
        $("#sheet").addClass("flipped");
    }
 
    $('#sheet').bind('webkitTransitionEnd',
        function( event ) {
            if ($("#sheet").hasClass("flipped")) $("#" + id).empty();
            else {
                $("#core_BACK").empty();
                $("#container").removeClass("container");
                $("#sheet").removeClass("sheet");
                $("#front").removeClass("face");
                $("#back").removeClass("face");
            }
            $('#sheet').unbind();
        },
        false
    );
 
}

Archivo CSS

Aquí tenemos el ipad.css completo. Lo puedes encontrar en la distribución de OpenXava, en la carpeta OpenXava/web/style/ipad.
body {
    margin: 0px; background: black;
}
 
#xava_loading, #xava_loading2 {
    padding: 20px;
        border: none;
    background: -webkit-gradient(linear, left bottom, left top,
        from(#10182C), color-stop(0.5, #121A2E),
        color-stop(0.51, #282F41), to(#7E838D));
    opacity:.60;
    -webkit-border-radius: 7px;
    color: white;
    text-shadow:0px -1px 1px rgba(0,0,0,0.75);
    font-weight: bold;
    -webkit-box-shadow: 10px 10px 10px rgba(140,140,140,0.75);;
    display:none;
    z-index: 9999;
    position: fixed;
    left: 45%
}
 
#xava_loading {
    top: 25%;
}
 
#xava_loading2 {
    top: 120%;
}
 
.ox-module {
    font-family: Helvetica, Arial, sans-serif;
}
 
 
/* Portrait */
@media screen and (orientation:portrait) {
    .ox-module, .ui-widget-content, #xava_loading, #xava_loading2 {
        font-size: 1.2em;
    }
    .ui-widget-content {
        font-size: 1.1em;
    }
    .ui-widget-header {
        font-size: 180%;
    }
    .ox-button-bar, .ox-section {
        height: 32px;
    }
    .ox-page-navigation-pages {
        top: 8px;
    }
    .ox-bottom-buttons input[type="button"] {
        -webkit-box-shadow: 0px 2px 0px white;
    }
    .ox-detail, .ox-frame-content, .ox-active-section {
        border: 2px solid #B2B4B8;
        -webkit-box-shadow: 0px 2px 0px white;
    }
    .ox-frame-actions img {
        height: 38px;
    }
}
 
 
 
/* Landscape */
@media screen and (orientation:landscape) {
    .ox-module {
        font-size: 0.9em;
    }
    .ui-widget-content, #xava_loading, #xava_loading2 {
        font-size: 0.95em;
    }
    .ui-widget-header {
        font-size: 150%;
    }
    .ox-button-bar, .ox-section {
        border-bottom: none;
        height: 25px;
    }
    .ox-page-navigation-pages {
        top: 11px;
    }
    .ox-bottom-buttons input[type="button"] {
        -webkit-box-shadow: 0px 1px 0px white;
    }
    .ox-button-bar-image img {
        height: 19px;
    }
    .ox-detail, .ox-frame-content, .ox-active-section {
        border: 1px solid #B2B4B8;
        border-top: 1px solid #8F8F8F;
        -webkit-box-shadow: 0px 1px 0px white;
    }
}
 
.ox-view {
    background: #D8D8D9;
    padding: 20px;
    border-bottom: 1px solid #959596;
}
 
.ox-detail {
    font-size: 130%;
 
}
 
.ox-detail, .ox-frame-content, .ox-active-section {
    background: white;
    padding: 10px;
    -webkit-border-radius:15px;
    color: #385587;
    text-shadow: 0px 1px 1px black #A0ADC4;
}
 
.ox-active-section {
    -webkit-border-top-left-radius: 0px;
    -webkit-border-top-right-radius: 0px;
    border-left: 1px solid #B2B4B8;
    border-right: 1px solid #B2B4B8;
}
 
.ox-frame-title-label {
    padding: 10px 0px 0px 10px;
    vertical-align: bottom;
}
 
input {
    color: #385587;
}
 
.ox-label {
    text-align: left;
    color: black;
    font-weight: bold;
    text-shadow: 0px 2px 1px #E8E8E9;
}
 
.ox-button-bar, .ox-collection-list-actions, .ox-section {
    font-size: 0.9em;
    padding: 10px 4px 0px 6px;
    border-top: 1px solid white;
    -webkit-border-top-left-radius: 7px;
    -webkit-border-top-right-radius: 7px;
    background: white;
    background: -webkit-gradient(linear, left bottom, left top,
        from(#A8ADB9), to(#F5F6F7));
}
.ox-collection-list-actions {
    padding: 0px 4px 0px 6px;
    height: 40px;
}
 
.ox-detail .ox-action-link {
    background: white;
    padding: 5px;
    border: 1px solid #C4C4C4;
    -webkit-border-radius:5px;
    color: #7C7A77;
    font-size: 0.9em;
    text-decoration: none;
    text-shadow:1px 0px 0px #EDEDED;
}
 
.ox-button-bar-button a, .ox-list .ox-action-link {
    margin-right: 5px;
    padding: 4px 9px 5px;
    border: 1px solid #51555B;
    border-top: 1px solid #4C4E50;
    -webkit-border-radius:5px;
    background: -webkit-gradient(linear, left bottom, left top,
        from(#6B737E), to(#B2B5BB));
    color: white;
    font-weight: bold;
    text-decoration:none;
    text-shadow:0px -1px 1px rgba(0,0,0,0.75);
}
 
.ox-button-bar-image {
    padding: 0px 15px 5px;
    vertical-align: top;
}
 
.ox-button-bar-mode-button, .ox-section-tab, .ox-section-link {
    padding: 4px 9px 5px;
    border: 1px solid #51555B;
    border-right: none;
    border-left: none;
    border-top: 1px solid #4C4E50;
    background: -webkit-gradient(linear, left bottom, left top,
        from(#A8ADB9), to(#F5F6F7));
    font-weight: bold;
    text-decoration:none;
    color: #5D6065;
    text-shadow:0px 1px 1px white;
}
 
.ox-active .ox-button-bar-mode-button, .ox-active .ox-section-tab {
    background: -webkit-gradient(linear, left bottom, left top,
        from(#6B737E), to(#B2B5BB));
    color: white;
    text-shadow:0px -1px 1px rgba(0,0,0,0.75);
}
 
.ox-first .ox-button-bar-mode-button, .ox-first .ox-section-tab {
    border-left: 1px solid #51555B;
    -webkit-border-top-left-radius: 5px;
    -webkit-border-bottom-left-radius: 5px;
}
 
.ox-last .ox-button-bar-mode-button, .ox-last .ox-section-tab {
    border-right: 1px solid #51555B;
    -webkit-border-top-right-radius: 5px;
    -webkit-border-bottom-right-radius: 5px;
}
 
.ox-list {
    font-size: 0.9em;
    border: none;
    padding: 2px;
    -webkit-border-radius:7px;
    -webkit-box-shadow: inset 1px 1px 4px #4E4E4E;
    color: #5D6065;
    font-weight: bold;
    text-shadow:0px 1px 1px white;
    background: #D8D8D9;
    -webkit-transition-property: -webkit-transform;
    -webkit-transition-duration: 1.5s;
}
 
.slided-left {
    -webkit-transform: translateX(-130%);
}
 
.slided-right {
    -webkit-transform: translateX(200%);
}
 
 
.ox-list-odd {
    background: #BBBBBB;
    border-top: 1px solid #CFCFCF;
    border-bottom: 1px solid #959596;
    padding: 10px 8px 10px 8px;
}
 
.ox-list-pair {
    background: #AFAFAF;
    border-top: 1px solid #C6C6C6;
    border-bottom: 1px solid #959596;
    padding: 10px 8px 10px 8px;
}
 
.ox-total-row {
    height: 40px;
    background: #AFAFAF;
    border-top: 1px solid #C6C6C6;
    border-bottom: 1px solid #959596;
    background: -webkit-gradient(linear, left bottom, left top,
        from(#565A63), color-stop(0.5, #6A6E76),
        color-stop(0.51, #797D84), to(#91969E));
}
 
.ox-total-cell, .ox-total-capable-cell {
    text-align: right;
    color: white;
    text-shadow:0px -1px 1px rgba(0,0,0,0.75);
}
 
.ox-list-header a {
    color: white;
    text-shadow:0px -1px 1px rgba(0,0,0,0.75);
}
 
.ox-list-header {
    background: -webkit-gradient(linear, left bottom, left top,
        from(#797D84), to(#91969E));
}
 
.ox-list-subheader {
    background: -webkit-gradient(linear, left bottom, left top,
        from(#565A63), to(#6A6E76));
    padding: 2px 8px 2px 10px;
}
 
.ox-list-title, .ox-module-description {
    font-weight: bold;
    font-size: 140%;
    color: #2C2F33;
    text-shadow:0px 2px 1px #E8E8E9;
    padding-left: 12px;
    padding-bottom: 12px;
}
 
.ox-module-description {
    color: #385587;
    text-shadow: 0px 1px 1px white;
 
}
 
.ox-header-list-count {
    margin-left: 10px;
    font-size: 75%;
}
 
.ox-list-info {
    margin-top: 10px;
    -webkit-border-radius:7px;
    border: 1px solid #505153;
    background: -webkit-gradient(linear, left bottom, left top,
        from(#565A63), color-stop(0.5, #6A6E76),
        color-stop(0.51, #797D84), to(#91969E));
}
 
.ox-page-navigation-arrow-disable, .ox-page-navigation-arrow img {
    padding: 5px 10px 7px 10px;
}
 
.ox-first .ox-page-navigation-arrow, .ox-first .ox-page-navigation-arrow-disable {
    border-right: 2px ridge #878B91;
    float: left;
}
 
.ox-last .ox-page-navigation-arrow, .ox-last .ox-page-navigation-arrow-disable {
    border-left: 2px groove #878B91;
    float: right;
}
 
.ox-page-navigation-pages {
    float:left;
    position:relative;
    left:40%;
}
 
.ox-page-navigation, .ox-page-navigation-selected {
    margin-right: 10px;
}
 
.ox-bottom-buttons {
    border-top: 1px solid white;
    background: #D8D8D9;
    padding: 10px;
    text-align: center;
}
 
.ox-bottom-buttons input[type="button"] {
    padding: 20px;
    margin: 10px;
    border: 1px solid #979797;
    -webkit-border-radius:10px;
    background: -webkit-gradient(linear, left bottom, left top,
        from(#D9D9D9), to(white));
    color: black;
    font-weight: bold;
    font-size: 110%;
}
 
.ox-errors {
    color: #CF0000;
    font-weight: bold;
    font-size: 140%;
    text-shadow:0px 2px 1px #E8E8E9;
}
 
.ox-messages {
    color: #2160DD;
    font-weight: bold;
    font-size: 140%;
    text-shadow:0px 2px 1px #E8E8E9;
}
 
.ox-filter-cell {
    text-align: left;
}
 
.ox-frame-totals {
    float: right;
}
.ox-frame-totals-label {
    font-weight: normal;
}
 
.ox-frame-totals-value {
    font-weight: bold;
}
 
.ui-widget-header {
    border: none;
    background: -webkit-gradient(linear, left bottom, left top,
        from(#10182C), color-stop(0.5, #121A2E),
        color-stop(0.51, #282F41), to(#7E838D));
    color: white;
    text-shadow:0px -1px 1px rgba(0,0,0,0.75);
    font-weight: bold;
}
 
.ui-corner-all {
    -webkit-border-radius: 10px;
}
 
.ui-widget-content {
    border: none;
    background: #151D31;
    -webkit-box-shadow: 15px 15px 30px rgba(140,140,140,0.75);;
}
 
.ui-widget-content .ox-view {
    -webkit-border-top-left-radius: 7px;
    -webkit-border-top-right-radius: 7px;
}
.ui-widget-content .ox-bottom-buttons {
    -webkit-border-bottom-left-radius: 7px;
    -webkit-border-bottom-right-radius: 7px;
}
 
.ui-dialog .ui-dialog-content {
    padding: 5px;
}
 
 
/* For flip effect */
.container {
    -webkit-perspective: 600;
}
 
 
.sheet {
    position: absolute;
     width: 100%;
}
 
 
.face {
    position: absolute;
    width: 100%;
    overflow: hidden;
}
 
 
.sheet {
    -webkit-transform-style: preserve-3d;
    -webkit-transition-property: -webkit-transform;
    -webkit-transition-duration: 1.5s;
}
 
 
.flipped
{
    -webkit-transform: rotateY(180deg);
}
 
#front, #back {
    -webkit-backface-visibility: hidden;
}
 
#back {
    -webkit-transform: rotateY(180deg);
}

Clase de estilo

Aquí está el código completo para la clase IPadStyle. Puedes encontrar esta clase en la distribución de OpenXava, en OpenXava/src/org.openxava.web.style.
package org.openxava.web.style;
 
/**
 * For iPads. <p>
 *
 * @author Javier Paniza
 */
 
public class IPadStyle extends Style {
 
    private static String [] jsFiles = {
        "ipad/ipad.js",
    };
 
    private static IPadStyle instance = null;
 
    protected IPadStyle() {
    }
 
    public static Style getInstance() {
        if (instance == null) {
            instance = new IPadStyle();
        }
        return instance;
    }
 
    public boolean isForBrowse(String browser) {
        return browser != null && browser.contains("iPad");
    }
 
    public boolean isOnlyOneButtonForModeIfTwoModes() {
        return true;
    }
 
    public boolean isSeparatorBeforeBottomButtons() {
        return false;
    }
 
    public String getSetHtmlFunction() {
        return "ipad.setHtml";
    }
 
 
    public String getCssFile() {
        return "ipad/ipad.css";
    }
 
    public String [] getNoPortalModuleJsFiles() {
        return jsFiles;
    }
 
 
    public String getInitThemeScript() {
        return "ipad.onLoad()";
    }
 
    public String getNextPageNavigationEvents(String listId) {
        return "onclick=ipad.onClickNextPage('" + listId + "')";
    }
 
    public String getPreviousPageNavigationEvents(String listId) {
        return "onclick=ipad.onClickPreviousPage('" + listId + "')";
    }
 
    public String getCoreStartDecoration() {
        return "<div id='container'><div id='sheet'><div id='front'>";
    }
 
    public String getCoreEndDecoration() {
        return
            "</div><div id='back'><div id='core_BACK' style='display: inline;' class='"
            + getModule() + "'></div></div></div></div>";
    }
 
    public String getDefaultModeController() {
        return "DetailList";
    }
 
    public boolean allowsResizeColumns() {
        return false;
    }
 
    public boolean isRowLinkable() {
        return false;
    }
 
    public boolean isShowPageNumber() {
        return false;
    }
 
    public boolean isShowModuleDescription() {
        return true;
    }
 
    public boolean isSeveralActionsPerRow() {
        return false;
    }
 
    public boolean isChangingPageRowCountAllowed() {
        return false;
    }
 
    public boolean isHideRowsAllowed() {
        return false;
    }
 
    public boolean isShowRowCountOnTop() {
        return true;
    }
 
    public boolean isUseLinkForNoButtonBarAction() {
        return true;
    }
 
    public boolean isHelpAvailable()  {
        return false;
    }
 
    public boolean isShowImageInButtonBarButton() {
        return false;
    }
 
    public boolean isUseStandardImageActionForOnlyImageActionOnButtonBar() {
        return true;
    }
 
    public boolean isFixedPositionSupported() {
        return false;
    }
 
 
    public String getMetaTags() {
        return "<meta name='apple-mobile-web-app-capable' content='yes'/>";
    }
 
 
    public String getModuleSpacing() {
        return "style='padding: 0px;'";
    }
 
    public String getListCellSpacing() {
        return "cellspacing=0 cellpadding=0";
    }
 
    public String getImagesFolder() {
        return "xava/style/ipad/images";
    }
 
}