Hojas de estilo

Desde Gaphor 2.0, los diagramas pueden tener un aspecto diferente mediante hojas de estilo. Las hojas de estilo usan la sintaxis Cascading Style Sheets (CSS). CSS se usa para describir la presentación de un documento escrito en un lenguaje de marcado, y se usa más comúnmente con HTML para páginas web.

En la página principal de CSS del W3C, CSS se describe como:

Cascading Style Sheets (CSS) es un mecanismo sencillo para añadir estilo (por ejemplo, fuentes, colores, espaciado) a los documentos web.

Pero su aplicación va mucho más allá de los documentos web. Gaphor usa CSS para proporcionar elementos de estilo a los elementos de los diagramas. CSS nos permite, a los usuarios de Gaphor, cambiar el aspecto visual de nuestros diagramas. Se pueden cambiar el color y los estilos de línea para facilitar la lectura de los diagramas.

Dado que se trata de un diagrama y no de un documento HTML, se han omitido algunas características CSS.

El estilo es parte del modelo, por lo que todos los que trabajen en un modelo tendrán el mismo estilo. Para editar el estilo pulse el botón de la página de herramientas en la esquina superior derecha en gaphor:

Botón para acceder al código de estilo

He aquí un ejemplo sencillo de cómo cambiar el color de fondo de una clase:

class {
  background-color: beige;
}
fondo beige

O cambiar el color de un componente, sólo cuando está anidado en un nodo:

node component {
  background-color: skyblue;
}
componente anidado

El propio diagrama también se expresa como un nodo CSS. Es bastante fácil definir un estilo «oscuro»:

diagram {
  background-color: #343131;
}

* {
  color: white;
  text-color: white;
}
estilo-oscuro

Aquí ya se ve el primer atributo personalizado: text-color. Esta propiedad le permite controlar el color del texto dibujado en un elemento. El color se usa para las líneas (trazos) que hacen el diseño de un elemento de diagrama.

Selectores compatibles

Dado que estamos tratando con diagramas y modelos, no necesitamos todas las características de CSS. A continuación encontrarás un resumen de todas las características CSS soportadas por Gaphor.

*

Todos los elementos del diagrama, incluido el propio diagrama.

componente nodo

Cualquier elemento componente descendiente de un nodo.

nodo > componente

Un elemento componente que es hijo de un nodo.

.item

Each diagram item has a class «item» assigned. This makes it easy to differentiate them from e.g. name elements.

generalization[subject]

Un elemento de generalización con un sujeto presente.

clase[nombre=Foo]

Una clase con el nombre «Foo».

diagrama[nombre^=borrador]

Un diagrama cuyo nombre empieza por «borrador».

diagrama[nombre$=borrador]

Un diagrama con nombre terminado en «borrador».

diagrama[nombre*=borrador]

Un diagrama con un nombre que contiene el texto «borrador».

diagrama[nombre~=borrador elemento]

Un diagrama con el nombre «borrador» o «elemento».

diagrama[nombre|=borrador]

Un diagrama con nombre «borrador» o que empieza por «borrador-«.

:focus

El elemento enfocado. Otras pseudo clases son:

  • :active elementos seleccionados

  • :hover para el elemento bajo el ratón

  • :drop si se arrastra un elemento y se puede soltar sobre este elemento

  • :disabled si un elemento está en gris durante el movimiento del manejador

:empty

Un nodo que no contiene nodos hijos en el diagrama.

:root

Refers to the diagram itself.

Esto solo es aplicable para el diagrama

:first-child

A node is the first element among a group of sibling.

:has()

El elemento contiene cualquiera de los selectores proporcionados.

P. ej., node:has(component): un nodo que contiene un elemento componente.

:is()

Coincide con cualquiera de los selectores proporcionados.

P. ej., :is(node, subsystem) > component: un nodo o subsistema.

:not()

Negar el selector.

P. ej., :not([subject]): Cualquier elemento que no tenga «tema».

::after

Provide extra content after a text. Only the content property is supported.

  • La especificación oficial de CSS3 attribute selectors.

  • Gaphor proporciona el selector de atributos |= en aras de la exhaustividad. Sin embargo, probablemente no sea muy útil en este contexto.

  • Tenga en cuenta que Gaphor CSS no admite ID para elementos de diagrama, por lo que no se usa la sintaxis CSS para ID (#some-id). Además, la sintaxis de clase (.some-class) no está soportada actualmente.

Propiedades de estilo

Gaphor soporta un subconjunto de propiedades CSS y algunas propiedades específicas de Gaphor. El intérprete de hojas de estilo es relativamente sencillo. Todas las anchuras, alturas y tamaños se miden en píxeles. No se pueden usar declaraciones de estilo complejas, como la propiedad font en HTML/CSS que puede contener familia de fuentes, tamaño, peso.

Some properties are inherited from the parent style. The parent often is a diagram. When you set a color`` or a font-familyondiagram`, it will propagate down to the items contained in the diagram.

Colores

background-color

Ejemplos:

background-color: azure;

background-color: rgb(255, 255, 255);

background-color: hsl(130, 95%, 10%);

color

Color used for lines. (inherited)

text-color

Color for text. (inherited)

Obsoleto desde la versión 2.23.0: Use color if possible.

opacity

Factor de opacidad del color (0.0 - 1.0), aplicado a todos los colores.

  • Un color puede ser cualquier código de color CSS3, como se describe en la documentación CSS. Gaphor soporta todas las notaciones de color: rgb(), rgba(), hsl(), hsla(), código hexadecimal (#ffffff) y nombres de color.

Texto y tipografías

font-family

A single font name (e.g. sans, serif, courier). (inherited)

font-size

An absolute size (e.g. 14) or a size value (e.g. small). (inherited)

font-style

Either normal or italic. (inherited)

font-weight

Either normal or bold. (inherited)

text-align

Either left, center, right. (inherited)

text-decoration

Ya sea none o underline.

vertical-align

Alineación vertical del texto.

Ya sea top, middle o bottom.

vertical-spacing

Establece el espaciado vertical para los elementos tipo icono (actores, estado inicial).

Ejemplo: vertical-spacing: 4.

white-space

Change the line wrapping behavior for text. (inherited)

  • font-family puede ser solo un nombre de tipografía, no una lista de nombres (fallback), como se usa para HTML.

  • font-size puede ser un número o valores CSS de tamaño absoluto. Solo se admiten los valores x-small, small, medium, large y x-large.

Trazado y espaciado

border-radius

Radio para rectángulos: border-radius: 4.

dash-style

Estilo para líneas discontinuas: dash-style: 7 5.

justify-content

Alineación del contenido de las cajas.

Ya sea start, end, center o stretch.

line-style

Ya sea normal o sloppy [factor].

line-width

Set the width for lines: line-width: 2. (inherited)

min-height

Establece la altura mínima de un elemento: min-height: 50.

min-width

Establece el ancho mínimo de un elemento: min-width: 100.

max-width

Set maximum width (text fields only): max-width: 100.

padding

Estilo CSS de relleno (arriba, derecha, abajo, izquierda).

Ejemplo: padding: 3 4.

  • padding está definido por enteros en el rango de 1 a 4. No es necesario usar ninguna unidad (px, pt, em). Todos los valores están en distancia de píxel.

  • dash-style es una lista de números (línea, hueco, línea, hueco, …)

  • line-style solo tiene efecto cuando se define en un diagram. Se puede proporcionar un factor de omisión en el rango de -2 a 2.

Pseudo elements

Currently, only the ::after pseudo element is supported.

content

Extra content to be shown after a text.

Variables

Desde Gaphor 2.16.0 puede usar variables CSS en sus hojas de estilo.

Esto le permite definir valores de uso frecuente de forma más genérica. Piense en cosas como el estilo de guiones de línea y los colores.

La función var() tiene algunas limitaciones:

  • Los valores no pueden tener un valor predeterminado.

  • Las variables no pueden tener una variable como valor.

Ejemplo:

diagram {
  --bg-color: whitesmoke;
  background-color: var(--bg-color);
}

diagram[diagramType=sd] {
  --bg-color: rgb(200, 200, 255);
}

Todos los diagramas tienen un fondo blanco. Los diagramas de secuencia tienen un fondo azulado.

Media queries

Gaphor soporta los modos oscuro y claro desde la versión 2.16.0. Los esquemas de color oscuro y claro se usan exclusivamente para la edición en pantalla. Al exportar imágenes, sólo se aplica el esquema de color predeterminado. Los esquemas de color se pueden definir con consultas @media. La consulta oficial prefers-color-scheme = dark está soportada, así como la más conveniente dark-mode.

/* The background you see in exported diagrams: */
diagram {
  background-color: transparent;
}

/* Use a slightly grey background in the editor: */
@media light-mode {
  diagram {
    background-color: #e1e1e1;
  }
}

/* And anthracite a slightly grey background in the editor: */
@media dark-mode {
  diagram {
    background-color: #393D47;
  }
}

Estilos de diagrama

En un diagrama solo pueden definirse unas pocas propiedades, a saber, background-color y line-style. El estilo del diagrama se define por separado de los estilos de los elementos del diagrama. De esta manera es posible establecer el color de fondo para los diagramas específicamente. El estilo de línea puede ser las líneas rectas normales, o un estilo «omisión» más juguetón. Para el estilo «omisión» se puede proporcionar un factor opcional de bamboleo para establecer el nivel de bamboleo de la línea. 0.5 es el valor predeterminado, 0.0 es una línea recta. El valor debe estar comprendido entre -2,0 y 2,0. Los valores entre 0,0 y 0,5 producen un efecto sutil.

CSS model elements

Gaphor tiene muchos elementos de modelo. ¿Cómo se puede saber qué elemento debe ser estilizado?

Gaphor solo aplica estilo a los elementos que están en el modelo, por lo que debe ser explícito en sus nombres. Por ejemplo: Component hereda de Class en el modelo UML, pero cambiar un color para Class no lo cambia para Component.

Si pasa el ratón por encima de un botón de la caja de herramientas (sección inferior izquierda), aparecerá una ventana emergente con el nombre del elemento y un acceso directo. Como regla general, puede usar el nombre del componente, pegado como el nombre en la hoja de estilos. Un Componente puede ser direccionado como component, Caso de uso como usecase. La coincidencia de nombres es insensible a mayúsculas y minúsculas. Los nombres CSS se escriben en minúsculas por defecto.

Sin embargo, como los nombres de los elementos CSS se derivan de los nombres usados en Gaphor, hay algunas excepciones.

Perfil

Grupo

Elemento

Elemento CSS

*

*

nombre del elemento

nombre del elemento sin espacios

P. ej. class, usecase.

UML

Clases

Todas las asociaciones

association

UML

Componentes

Dispositivo/Nodo

node

UML

Acciones

Nodo de decisión/fusión

decisionnode

UML

Acciones

Nodo de bifurcación/unión

forknode

UML

Acciones

Carril

partition

UML

Interacciones

mensaje reflexivo

message

UML

Estados

Pseudoestado inicial

pseudostate

UML

Estados

Pseudoestado de historia

pseudostate

UML

Perfiles

Metaclase

class

Modelo C4

Modelo C4

Persona

c4person

Modelo C4

Modelo C4

Sistema informático

c4container[type="Software System"]

Modelo C4

Modelo C4

Componente

c4container[type="Component"]

Modelo C4

Modelo C4

Contenedor

c4container[type="Container"]

Modelo C4

Modelo C4

Contenedor: Base de datos

c4database

SysML

Bloques

ValorTipo

datatype

SysML

Bloques

Primitivo

datatype

SysML

Requisitos

Derivación de requisitos

derivedreq

RAAML

FTA

cualquier puerta AND/OR/…

and, or, etc.

Ideas

He aquí algunas ideas que van más allá de cambiar un color o un tipo de letra. Con los siguientes ejemplos profundizamos en la estructura del modelo de Gaphor para revelar más información a los usuarios.

To create your own expression you may want to use the Console (Menu icon → Tools → Console). Drop us a line on Gitter and we would be happy to help you.

El paquete de borradores

Todos los diagramas del paquete «Borradores» deben dibujarse con líneas poco definidas:

diagram[owner.name=drafts] {
  line-style: sloppy 0.3;
}

diagram[owner.name=drafts] * {
  font-family: Purisa; /* Or use some other font that's installed on your system */
}
estilo borrador

Wrap long attributes, operations, and stereotype values

Sometimes attribute values get a bit lengthy. By default, Gaphor will not wrap text. If you want to, you can add this little snippet to wrap text for lengthy attributes on classes. You can also apply this to other types, of course.

class * {
 white-space: normal;
}
wrap long attribute

Relaciones inconexas

Todos los elementos de un diagrama que no estén respaldados por un elemento del modelo deben dibujarse en color rojo oscuro. Esto se puede usar para detectar relaciones no muy bien conectadas, como Generalización, Implementación y Dependencia. Estos elementos sólo estarán respaldados por un elemento del modelo una vez que conecte ambos extremos de la línea. Esta regla excluirá elementos simples, como líneas y cajas, que nunca tendrán un elemento modelo de respaldo.

:not(:is(:root, line, box, ellipse, commentline))[subject=""] {
  color: firebrick;
}
relación inconexa

Líneas sólidas de flujo de control

En Gaphor, las líneas de flujo de control siguen el estilo SysML: discontinuas. Si desea, o necesita seguir estrictamente las especificaciones oficiales UML, puede simplemente hacer esas líneas sólidas.

controlflow {
  dash-style: 0;
}
flujo de control

Nota destacada

Todos los comentarios que empiecen por la frase «todo» pueden resaltarse con un color diferente específico para cada usuario. Esto puede usarse para avisarle de que tiene que hacer algún trabajo adicional para finalizar el diagrama.

comment[body^="TODO"] {
  background-color: skyblue;
}
nota destacada

Emphesize abstract classes and operations

It may be that the italic font used is not distinguishable enough to differentiate between concrete and abstract classes or operations. To make this work we check if the isAbstract attribute is set on the element:

:is(name, operation)[isabstract]::after {
  content: " {abstract}"
}
emphasize abstract elements

System Style Sheet

/* Gaphor diagram style sheet */

* {
  --opaque-background-color: white;
  background-color: transparent;
}

:not(diagramframe):drop {
  color: #1a5fb4;
  line-width: 3;
}

:disabled {
  opacity: 0.5;
}

@media light-mode {
  * {
    --opaque-background-color: #fafafa;
  }
}

@media dark-mode {
  * {
    --opaque-background-color: #242424;
    color: white;
  }

  :drop {
    color: #62a0ea;
  }
}

:root {
  color: black;
  font-family: sans;
  font-size: 14 ;
  line-width: 2;
  padding: 0;
}

diagramframe {
  justify-content: start;
}

:is(:root, diagramframe) > pentagon {
  line-width: 1;
  background-color: var(--opaque-background-color);
}

:is(:root, diagramframe) > pentagon > diagramtype {
  font-weight: bold;
  padding: 4 0 4 4;
}

:is(:root, diagramframe) > pentagon > stereotypes {
  padding: 4 0 4 4;
}

:is(:root, diagramframe) > pentagon > name {
  padding: 4;
}

:has(.item) compartment:first-child {
  justify-content: start;
}

/* Relationships */

commentline,
c4dependency,
dependency,
interfacerealization,
include,
extend,
packageimport,
lifetime,
satisfy,
derivereqt,
trace,
verify,
refine {
 dash-style: 7 5;
}

dependency[on_folded_interface = true],
interfacerealization[on_folded_interface = true] {
  dash-style: 0;
}

/* General */

comment {
  text-align: left;
  vertical-align: top;
  padding: 4 16 4 4;
}

comment stereotypes {
  text-align: center;
}

comment body {
  padding: 0;
}

diagram > icon {
  padding: 4;
  border-radius: 4;
}

diagram > type {
  font-weight: bold;
}

metadata {
  justify-content: stretch;
  text-align: left;
}

metadata cell {
  padding: 4;
}

metadata heading {
  font-weight: bold;
  font-style: normal;
  font-size: small;
}

pentagon {
  padding: 4;
  justify-content: start;
  text-align: left;
}

/* UML */

controlflow {
  dash-style: 9 3;
}

objectnode > icon {
  padding: 4 12;
}

decisionnode > type {
  font-size: small;
}

proxyport > icon,
activityparameternode,
executionspecification {
  background-color: var(--opaque-background-color);
}

partition {
  padding: 4 12 4 12;
  justify-content: stretch;
}

package {
  padding: 24 12 4 12;
}

interaction {
  justify-content: start;
}

activity {
  padding: 4 12;
  border-radius: 20;
  justify-content: start;
}

activityparameternode {
  padding: 4 12;
  min-width: 120;
  text-align: center;
}

action,
valuespecificationaction {
  padding: 4 12;
  border-radius: 15;
}

callbehavioraction {
  padding: 4 24 4 12;
  border-radius: 15;
}

sendsignalaction {
  padding: 4 24 4 12;
}

accepteventaction {
  padding: 4 12 4 24;
}

usecase {
  padding: 4;
}

swimlane {
  min-width: 150;
  padding: 4 12 4 12;
  justify-content: start;
  white-space: normal;
}

association > end {
  font-size: x-small;
  padding: 4;
}

/* SysML */

requirement {
  justify-content: start;
}

requirement text {
  white-space: normal;
}

directedrelationshippropertypath {
  dash-style: 7 5;
}

/* Classifiers */

compartment:first-child {
  padding: 12 4;
}

compartment + compartment {
  padding: 4;
  min-height: 8;
  text-align: left;
  justify-content: start;
  white-space: nowrap;
}

artifact compartment:first-child,
component compartment:first-child {
  padding: 12 24 12 4;
}

state compartment:first-child {
  padding: 4;
}

:has(.item),
:has(compartment + compartment),
:has(regions) {
  justify-content: start;
}

regions {
  justify-content: stretch;
}

region {
  padding: 4;
  min-height: 100;
  justify-content: start;
  text-align: left;
}

region + region {
  dash-style: 7 3;
}

and name,
xor name,
intermediateevent name,
dormantevent name,
basicevent name,
houseevent name,
topevent name,
inhibit name,
conditionalevent name,
zeroevent name,
or name,
not name,
transferin name,
transferout name,
undevelopedevent name,
seq name,
majorityvote name,
unsafecontrolaction name,
operationalsituation name,
controlaction name,
interfaceblock name,
block name,
property name,
requirement name,
c4person name,
c4database name,
c4container name,
package name,
enumeration name,
interface name,
class name,
datatype name,
component name,
statemachine name,
usecase name,
actor name,
artifact name,
node name {
  font-weight: bold;
}

name[isabstract] {
  font-style: italic;
}

from {
  font-size: x-small;
}

activity > :is(name, stereotypes) {
  text-align: left;
}

compartment heading {
  padding: 0 0 4 0;
  font-size: x-small;
  font-style: italic;
  text-align: center;
}

operation[isabstract] {
  font-style: italic;
}

attribute[isstatic],
operation[isstatic] {
  text-decoration: underline;
}

property:not([aggregation="composite"]) {
  dash-style: 7 5;
}

/* Attached */

:has(icon)[connected_side] {
  text-align: right;
  vertical-align: top;
}

:has(icon)[connected_side="left"] {
  text-align: left;
}

:has(icon)[connected_side="bottom"] {
  vertical-align: bottom;
}

/* C4 model */

c4container, c4person {
  padding: 4 4 4 4;
}

c4database {
  padding: 20 4 4 4;
}

:is(c4container, c4database, c4person):has(.item) {
  justify-content: end;
}

:is(c4container, c4database, c4person):has(.item) > :is(name, technology) {
  text-align: left;
}

c4dependency name {
  max-width: 150;
}

:is(c4container, c4database, c4dependency, c4person) technology {
  font-size: x-small;
}

:is(c4container, c4database, c4person) description {
  padding: 4 4 0 4;
}