Custom Components

Extend the Bootstrap plugin by creating specialized DITA elements that map to specific Bootstrap components, such as Callouts.

This scenario walks through the process of creating a custom component plug-in (com.example.bootstrap-component) that defines a new <callout> element. This specialized element automatically applies Bootstrap utility classes.

Procedure

  1. In the plugins directory, create a directory structure like the following:

    com.example.bootstrap-component
    ├── Customization
    │   └── xsl
    │       └── callout.xsl
    ├── dtd
    │   └── base
    │       ├── bootstrap-custom-topic.dtd
    │       ├── bootstrapCustomDomain.ent
    │       └── bootstrapCustomDomain.mod
    ├── catalog.xml
    ├── plugin.xml
    └── xsl
        └── xhtml.xsl
    Figure 1. Plugin directory structure
  2. Define the specialization entities in bootstrapCustomDomain.ent:

    <?xml version="1.0" encoding="UTF-8"?>
    <!-- MODULE: DITA Bootstrap Custom Domain Entities -->
    
    <!ENTITY % callout "callout">
    <!ENTITY % bootstrapCustom-d-section "callout">
    <!ENTITY bootstrapCustom-d-att "(topic bootstrapCustom-d)">
    Figure 2. Sample bootstrapCustomDomain.ent file
  3. Define the specialization module in bootstrapCustomDomain.mod:

    <!-- ============================================================= -->
    <!--                   BOOTSTRAP CUSTOM DOMAIN                     -->
    <!-- ============================================================= -->
    
    <!-- ============================================================= -->
    <!--                    ELEMENT DECLARATIONS                       -->
    <!-- ============================================================= -->
    
    <!ENTITY % callout.content "(%section.cnt;)*">
    <!ENTITY % callout.attributes
      "spectitle CDATA #IMPLIED
       color CDATA #IMPLIED
       rounded (yes | no | 0 | 1 | 2 | 3 | 4 | 5 | circle | pill) #IMPLIED
       outputclass CDATA #IMPLIED
       %univ-atts;">
    <!ELEMENT callout %callout.content;>
    <!ATTLIST callout %callout.attributes;>
    
    <!-- ============================================================= -->
    <!--                    SPECIALIZATION ATTRIBUTE DECLARATIONS      -->
    <!-- ============================================================= -->
    
    <!ATTLIST callout class CDATA "+ topic/section bootstrapCustom-d/callout ">
    Figure 3. Sample bootstrapCustomDomain.mod file
  4. Create a DTD Shell (bootstrap-custom-topic.dtd) to integrate the domain. The order of declarations is critical due to the XML DTD "First-Win" rule, which specifies that only the first definition of a parameter entity is authoritative.

    The DTD shell must follow this specific sequence to ensure specializations are correctly recognized:

    1. Domain Entities: Declare the entities for your custom domain.
    2. Domain Extensions: Redefine the base content models (e.g., %section;) to include your new elements. This MUST happen before structural modules are loaded.
    3. Structural Modules: Finally, import the base topic.mod and your custom .mod file.
    <?xml version="1.0" encoding="UTF-8"?>
    <!-- ============================================================= -->
    <!--                    BOOTSTRAP CUSTOM TOPIC DTD SHELL           -->
    <!-- ============================================================= -->
    
    <!-- 1. ELEMENT NAME ENTITIES -->
    <!ENTITY % callout "callout">
    
    <!-- 2. DOMAIN ENTITY DECLARATIONS -->
    <!ENTITY % hi-d-dec PUBLIC "-//OASIS//ENTITIES DITA 1.3 Highlight Domain//EN" "highlightDomain.ent">%hi-d-dec;
    <!ENTITY % ut-d-dec PUBLIC "-//OASIS//ENTITIES DITA 1.3 Utilities Domain//EN" "utilitiesDomain.ent">%ut-d-dec;
    <!ENTITY % indexing-d-dec PUBLIC "-//OASIS//ENTITIES DITA 1.3 Indexing Domain//EN" "indexingDomain.ent">%indexing-d-dec;
    <!ENTITY % hazard-d-dec PUBLIC "-//OASIS//ENTITIES DITA 1.3 Hazard Statement Domain//EN" "hazardstatementDomain.ent">%hazard-d-dec;
    <!ENTITY % sw-d-dec PUBLIC "-//OASIS//ENTITIES DITA 1.3 Software Domain//EN" "softwareDomain.ent">%sw-d-dec;
    <!ENTITY % pr-d-dec PUBLIC "-//OASIS//ENTITIES DITA 1.3 Programming Domain//EN" "programmingDomain.ent">%pr-d-dec;
    <!ENTITY % ui-d-dec PUBLIC "-//OASIS//ENTITIES DITA 1.3 User Interface Domain//EN" "uiDomain.ent">%ui-d-dec;
    <!ENTITY % xml-d-dec PUBLIC "-//OASIS//ENTITIES DITA 1.3 XML Domain//EN" "xmlDomain.ent">%xml-d-dec;
    
    <!-- Bootstrap and Custom Callout entities -->
    <!ENTITY % bootstrap-d-dec PUBLIC "-//Infotexture//ENTITIES DITA Bootstrap Domain//EN" "bootstrapDomain.ent">%bootstrap-d-dec;
    <!ENTITY % bootstrapCustom-d-dec PUBLIC "//Example//ENTITIES DITA Bootstrap Custom Domain//EN" "bootstrapCustomDomain.ent">%bootstrapCustom-d-dec;
    
    <!-- 3. DOMAIN ATTRIBUTES DECLARATIONS -->
    <!ENTITY % deliveryTargetAtt-d-dec PUBLIC "-//OASIS//ENTITIES DITA 1.3 Delivery Target Attribute Domain//EN" "deliveryTargetAttDomain.ent">%deliveryTargetAtt-d-dec;
    
    <!-- 4. DOMAIN EXTENSIONS - TOP DEFINTIONS WIN -->
    <!ENTITY % div          "div | %bootstrap-d-div;">
    <!ENTITY % ph           "ph | %hi-d-ph; | %pr-d-ph; | %bootstrap-d-ph; | %sw-d-ph; | %ui-d-ph;">
    <!ENTITY % xref         "xref | %bootstrap-d-xref;">
    <!ENTITY % ul           "ul | %bootstrap-d-ul;">
    <!ENTITY % li           "li | %bootstrap-d-li;">
    <!ENTITY % image        "image | %bootstrap-d-image;">
    <!ENTITY % fig          "fig | %ut-d-fig; | %pr-d-fig;">
    <!ENTITY % keyword      "keyword | %xml-d-keyword; | %pr-d-keyword; | %sw-d-keyword; | %ui-d-keyword;">
    <!ENTITY % pre          "pre | %pr-d-pre; | %sw-d-pre; | %ui-d-pre;">
    <!ENTITY % dl           "dl | %pr-d-dl;">
    <!ENTITY % bodydiv      "bodydiv | %bootstrap-d-bodydiv;">
    <!ENTITY % section      "section | %bootstrap-d-section; | %bootstrapCustom-d-section;">
    
    <!-- 5. DOMAIN ATTRIBUTE EXTENSIONS -->
    <!ENTITY % props-attribute-extensions "%deliveryTargetAtt-d-attribute;">
    
    <!-- 6. TOPIC ELEMENT INTEGRATION -->
    <!ENTITY % topic-mod-def PUBLIC "-//OASIS//ELEMENTS DITA 1.3 Topic//EN" "topic.mod">%topic-mod-def;
    
    <!-- 7. DOMAIN ELEMENT INTEGRATION -->
    <!ENTITY % bootstrap-d-def PUBLIC "-//Infotexture//ELEMENTS DITA Bootstrap Domain//EN" "bootstrapDomain.mod">%bootstrap-d-def;
    <!ENTITY % bootstrapCustom-d-def PUBLIC "//Example//ELEMENTS DITA Bootstrap Custom Domain//EN" "bootstrapCustomDomain.mod">%bootstrapCustom-d-def;
    
    <!ENTITY % hi-d-def PUBLIC "-//OASIS//ELEMENTS DITA 1.3 Highlight Domain//EN" "highlightDomain.mod">%hi-d-def;
    <!ENTITY % ut-d-def PUBLIC "-//OASIS//ELEMENTS DITA 1.3 Utilities Domain//EN" "utilitiesDomain.mod">%ut-d-def;
    <!ENTITY % indexing-d-def PUBLIC "-//OASIS//ELEMENTS DITA 1.3 Indexing Domain//EN" "indexingDomain.mod">%indexing-d-def;
    <!ENTITY % hazard-d-def PUBLIC "-//OASIS//ELEMENTS DITA 1.3 Hazard Statement Domain//EN" "hazardstatementDomain.mod">%hazard-d-def;
    <!ENTITY % sw-d-def PUBLIC "-//OASIS//ELEMENTS DITA 1.3 Software Domain//EN" "softwareDomain.mod">%sw-d-def;
    <!ENTITY % pr-d-def PUBLIC "-//OASIS//ELEMENTS DITA 1.3 Programming Domain//EN" "programmingDomain.mod">%pr-d-def;
    <!ENTITY % ui-d-def PUBLIC "-//OASIS//ELEMENTS DITA 1.3 User Interface Domain//EN" "uiDomain.mod">%ui-d-def;
    <!ENTITY % xml-d-def PUBLIC "-//OASIS//ELEMENTS DITA 1.3 XML Domain//EN" "xmlDomain.mod">%xml-d-def;
    
    <!-- Specialization of element ancestors -->
    <!ENTITY included-domains "&hi-d-att; &ut-d-att; &indexing-d-att; &hazard-d-att; &sw-d-att; &pr-d-att; &ui-d-att; &xml-d-att; &bootstrap-d-att; &bootstrapCustom-d-att; &deliveryTargetAtt-d-att;">
    Figure 4. Sample bootstrap-custom-topic.dtd file
  5. Implement the component-specific XSLT transformation in callout.xsl. Use a high priority to override default behavior as necessary:

    <!-- Handle the callout element transformation -->
    <xsl:template match="*[contains(@class, 'bootstrapCustom-d/callout')]">
      <div>
        <xsl:call-template name="commonattributes"/>
        <xsl:call-template name="setidaname"/>
        <!-- Add special handling as necessary -->
        <xsl:apply-templates/>
      </div>
    </xsl:template>
    
    <!-- Add custom output class handling for the new component -->
    <xsl:template match="*[contains(@class, 'bootstrapCustom-d/callout')]" mode="set-output-class" priority="50">
      <xsl:variable name="color" select="(if (@color) then @color else 'primary')"/>
      <xsl:variable name="classes" as="xs:string*">
         <xsl:sequence select="tokenize('callout p-2 border-start border-5', '\s+')"/>
         <xsl:sequence select="concat('bg-', $color, '-subtle')"/>
         <xsl:sequence select="concat('border-', $color)"/>
         <xsl:sequence select="concat('callout-', $color)"/>
         <xsl:sequence select="tokenize(normalize-space($ancestry), '\s+')"/>
      </xsl:variable>
      <xsl:attribute name="class" select="normalize-space(string-join(distinct-values($classes), ' '))"/>
    </xsl:template>
    Figure 5. Sample callout.xsl file
  6. Create a central hub in xsl/xhtml.xsl to include your custom transformations:

    <?xml version="1.0" encoding="utf-8"?>
    <xsl:stylesheet
      version="2.0"
      xmlns:dita-ot="http://dita-ot.sourceforge.net/ns/201007/dita-ot"
      xmlns:xs="http://www.w3.org/2001/XMLSchema"
      xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
      exclude-result-prefixes="xs dita-ot"
    >
      <xsl:include href="../Customization/xsl/callout.xsl"/>
    
    </xsl:stylesheet>
    Figure 6. Sample xhtml.xsl hub file
  7. Register the plugin hub in plugin.xml using the dita.xsl.html5-bootstrap extension point:

    <plugin id="com.example.bootstrap-component">
      <feature extension="dita.xsl.html5-bootstrap" value="xsl/xhtml.xsl" type="file" />
      <feature extension="dita.specialization.catalog.relative" file="catalog.xml"/>
    </plugin>
    Figure 7. Sample plugin.xml file

Example Usage

Once installed, authors can use the specialized element in DITA content:

<callout color="success">
  <title>Success!</title>
  <p>Further text can be added here.</p>
</callout>
Figure 8. Specialized callout usage
Figure 9. Rendered Callout Component