Export static html file with tabs macro

I would like to export my TW which have lots of tabs macro into static html file. However, it seems TW can only view the first tab in the generated html file.

Step to reproduce it

All tabs are shown, but cannot click/view other tabs.

Are there any solutions for this problem?

The tabs macro uses the reveal widget which is js based. When creating static tiddlers no js is exported, so the standard tabs macro will not work.

You could write a css based tabs macro. As a starter you could try this macro and style tiddler:

macro:

\procedure csstabmacro(list)
<div class="tabs">
  <div class="tab-buttons tc-tab-buttons ">
  <$list filter="[enlist<list>]" variable=ind counter="count">  
  <%if [<count>compare:number:lt[2]] %>
      <input   type="radio" id=<<ind>> name=<<currentTiddler>> checked />
   <%else%>
      <input   type="radio" id=<<ind>> name=<<currentTiddler>>  />
   <%endif%>
    <label for=<<ind>>><$transclude tiddler=<<ind>> field=caption><<ind>></$transclude></label>
    </$list>
  </div>
  <div class="tab-content">
  <$list filter="[enlist<list>]" variable=ind>
    <div>
    
    <$transclude tiddler=<<ind>>/>
    
    </div>
   </$list>
     </div>
</div>
\end

style tiddler


.tabs {
  display: block;
}

.tab-buttons {
  display: block;
  white-space: nowrap;
}

.tab-buttons input {
  display: none; /* Hide the radio buttons */
}

.tab-buttons label {
  display: inline-block; 
  padding: 10px 20px;
  cursor: pointer;
  background: #ddd;
  border: 1px solid #ccc;
  font-size: 16px;
}

.tab-buttons label:hover {
  background: #ccc;
}

.tab-content > div {
  display: none; 
  padding: 20px;
  background: #f5f5f5;
  border: 1px solid #ddd;
}
/* content for the selected tab */
.tabs:has(.tab-buttons input:nth-child(1):checked ) .tab-content > div:nth-child(1),
.tabs:has(.tab-buttons input:nth-child(3):checked ) .tab-content > div:nth-child(2),
.tabs:has(.tab-buttons input:nth-child(5):checked ) .tab-content > div:nth-child(3),
.tabs:has(.tab-buttons input:nth-child(7):checked ) .tab-content > div:nth-child(4),
.tabs:has(.tab-buttons input:nth-child(9):checked ) .tab-content > div:nth-child(5),
.tabs:has(.tab-buttons input:nth-child(11):checked ) .tab-content > div:nth-child(6){
  display: block;
}

/*  selected tag */
.tab-buttons input:nth-child(1):checked + label,
.tab-buttons input:nth-child(3):checked + label,
.tab-buttons input:nth-child(5):checked + label,
.tab-buttons input:nth-child(7):checked + label,
.tab-buttons input:nth-child(9):checked + label,
.tab-buttons input:nth-child(11):checked + label {
  background: #ccc;
  font-weight: bold;
}

2 Likes

See also this discussion: Exemplary doc structure

Thanks It is what I want.

1 Like

This is a modify version for csstabmacro to

  • use a filter instead a list of tiddlers
  • fix a bug if there are multiple tabs in the story river.
\function tabID() [<uniqueId>] [[_tab_]] [<count>] +[join[]]


\procedure csstabmacro(filter)

<$vars uniqueId=<<currentTiddler>> >

<div class="tabs">
  <div class="tab-buttons tc-tab-buttons">
    <$list filter=<<filter>> variable="ind" counter="count">
      <$vars tabId=<<tabID>> >
        <%if [<count>compare:number:lt[2]] %>
          <input type="radio" id=<<tabId>> name=<<uniqueId>> checked />
        <%else%>
          <input type="radio" id=<<tabId>> name=<<uniqueId>> />
        <%endif%>
       <label for=<<tabId>>><$transclude tiddler=<<ind>> field=caption><<ind>></$transclude></label>
      </$vars>
    </$list>
  </div>

  <div class="tab-content">
    <$list filter=<<filter>> variable="ind" counter="count">
      <div>
        <$transclude tiddler=<<ind>> />
      </div>
    </$list>
  </div>
</div>
</$vars>
\end

Updates

  • Remove space in the tab unique ID
  • Fix css which does not show dynamic content generated by js library (e.g. leaflet map although leaflet still cannot be exported).

csstabmacro:

\function tabID() [<uniqueId>] [[_tab_]] [<count>] +[join[]]


\procedure csstabmacro(filter)

<$set name="uniqueId" filter="[<currentTiddler>search-replace:g:regexp[ ],[]]" >
<<uniqueId>>
<div class="tabs">
  <div class="tab-buttons tc-tab-buttons">
    <$list filter=<<filter>> variable="ind" counter="count">
      <$vars tabId=<<tabID>> >
        <%if [<count>compare:number:lt[2]] %>
          <input type="radio" id=<<tabId>> name=<<uniqueId>> checked />
        <%else%>
          <input type="radio" id=<<tabId>> name=<<uniqueId>> />
        <%endif%>
       <label for=<<tabId>>><$transclude tiddler=<<ind>> field=caption><<ind>></$transclude></label>
      </$vars>
    </$list>
  </div>

  <div class="tab-content">
    <$list filter=<<filter>> variable="ind" counter="count">
      <div class="tab-panel" data-tabid=<<tabID>>>
        <$transclude tiddler=<<ind>> />
      </div>
    </$list>
  </div>
</div>
</$set>
\end

csstabstyle:

.tabs {
  display: block;
  position: relative; /* Ensures sticky positioning works */
  min-height: 300px; /* Ensures enough height for scrolling */
}

.tab-buttons {
  display: block;
  white-space: nowrap;
  position: sticky;
  top: 0;
  z-index: 100;
  padding: 5px 0;
}

.tab-buttons input {
  display: none; /* Hide the radio buttons */
}

.tab-buttons label {
  display: inline-block;
  padding: 10px 20px;
  cursor: pointer;
  background: #ddd;
  border: 1px solid #ccc;
  font-size: 16px;
  border-radius: 8px 8px 0 0; /* Rounded corners on the top */
}

.tab-buttons label:hover {
  background: #ccc;
}


.tab-content > div {
  opacity: 0;
  visibility: hidden;
  height: 0;
  padding: 0 20px;
  background: #f5f5f5;
  border: 1px solid #ddd;
  border-radius: 0 0 8px 8px;
  transition: opacity 0.3s ease-in-out, height 0.3s ease-in-out, padding 0.3s ease-in-out;
  overflow: hidden; /* Keep overflow hidden when not active */
}


/* content for the selected tab */
.tabs:has(.tab-buttons input:nth-child(1):checked ) .tab-content > div:nth-child(1),
.tabs:has(.tab-buttons input:nth-child(3):checked ) .tab-content > div:nth-child(2),
.tabs:has(.tab-buttons input:nth-child(5):checked ) .tab-content > div:nth-child(3),
.tabs:has(.tab-buttons input:nth-child(7):checked ) .tab-content > div:nth-child(4),
.tabs:has(.tab-buttons input:nth-child(9):checked ) .tab-content > div:nth-child(5),
.tabs:has(.tab-buttons input:nth-child(11):checked ) .tab-content > div:nth-child(6){
  opacity: 1;
  visibility: visible;
  height: auto;
  padding: 20px; /* Ensure padding is correctly applied */
  overflow: visible;
}

/*  selected tag */
.tab-buttons input:nth-child(1):checked + label,
.tab-buttons input:nth-child(3):checked + label,
.tab-buttons input:nth-child(5):checked + label,
.tab-buttons input:nth-child(7):checked + label,
.tab-buttons input:nth-child(9):checked + label,
.tab-buttons input:nth-child(11):checked + label {
  background: #ccc;
  font-weight: bold;
}