Placard
A javascript declarative CSS FlexBox (Flexible Layout) based layout manager for generating dynamic SVGs and images. Implemented using {{mustache}} and yoga-layout
Install / Use
/learn @jaladankisuresh/PlacardREADME
placard
A javascript CSS FlexBox (Flexible Layout) based declarative layout managers for generating dynamic SVGs and images. Its been designed for adding dynamic media content to Posters, Flyers, Placards or anyother digital prints generated through Photoshop or Pixlr like SVG generating apps. Built using {{mustache}} and yoga.
Re-define how and where you are using SVGs
SVGs are great for displaying vector graphics and for data visualizations like graphs, charts. But, With its XML format and CSS styling it could be used in diverse use cases. Placard is built leveraging its serialized data representation structure (i.e XML and CSS) that is easy to manipulate dynamically;
Using it as data-driven template and layout manager
- Mustache templating to bind data dynamically
- Yoga [CSS FlexBox based layout engine] for dynamically positioning elements in the SVG
Usage
Placard Libarary uses Yoga under-the-hood for generating layouts, and for calculating X, Y, WIDTH and HEIGHT attributes for XML elements. However, unlike Yoga which follows camelCase naming convention, Placard follows CSS Flexible Box Layout naming standards.
Placard implements the following CSS FlexBox properties; It mostly likely has one-to-one mapping with every Yoga Flex property
align-content
align-items
align-self
flex
flex-direction
flex-flow
flex-grow
flex-shrink
flex-wrap
justify-content
Additions (specific to this library)
position-type for setting child position to either absolute or relative. refer yoga documentation
Set x, y, width and height Explicity for Skiping Use explict attribute setting to avoid placard from calculating X, Y, WIDTH and HEIGHT for the element and setting the same.
<!-- Placard would respect these attributes and would not try to calculate these values -->
<image x="60%" y="10%" width="60" height="60"
xlink:href="./assets/icons/smiling.png"/>
Hello {{SVG}}! Example
SET flex-css attribute
Placard parses flex-css attribute defined in the elements looking for CSS FlexBox properties, and calculates X,Y, WIDTH and HEIGHT for the element
SVG template
<svg xmlns="http://www.w3.org/2000/svg" version="1.1"
xmlns:xlink="http://www.w3.org/1999/xlink" height="630" style="background-color:#eeeeee"
width="1200" flex-css="padding: 20;flex-direction: column;justify-content:center;align-items:center;">
<svg flex-css="flex-direction: column;width:600;aspect-ratio:1;align-items:center;">
<rect flex-css="position-type: absolute;width:100%;height:100%"
style="fill:#fafbfc;stroke:#000000;stroke-width:0.5;stroke-opacity:1" />
<rect flex-css="width:20%;aspect-ratio:0.5;"
style="fill:#40e0d0;stroke-width:0.5" />
<svg flex-css="width:100%;aspect-ratio:4;justify-content:center;align-items:center;">
<rect flex-css="position-type: absolute;width:100%;height:100%"
style="fill:#ffc0cb;stroke-width:0.5" />
<text style="font-size:50">Hello {{name}}!</text>
</svg>
<rect flex-css="width:20%;aspect-ratio:0.5;"
style="fill:#40e0d0;stroke-width:0.5" />
</svg>
</svg>
Data binding and Executing
let helloSVGTemplate = fs.readFileSync('templates/hello-svg-template.svg', 'utf-8');
let svgContentString = Mustache.render(helloSVGTemplate, {name : 'SVG'});
var xmlDoc = new DOMParser().parseFromString(svgContentString,'text/xml');
placard.svgTemplatingCtrl.requestYogaLayout(xmlDoc);
let svgLayoutCompleteString = new XMLSerializer().serializeToString(xmlDoc);
fs.writeFile('hello-svg.svg', svgLayoutCompleteString, function() {
console.log('output svg written to out.svg');
});
<img src="hello-svg.png" width="600">
Nested Layouts Example
SVG template
<svg xmlns="http://www.w3.org/2000/svg" version="1.1"
xmlns:xlink="http://www.w3.org/1999/xlink" height="630" style="background-color:#eeeeee"
width="1200" flex-css="padding: 20;flex-direction: row;">
<svg class="left-column-layout" flex-css="padding-top:10;padding-left:10;padding-bottom:10;flex-direction: column;flex: 1;">
<svg flex-css="flex-direction: column;flex:none;align-items:center;justify-content:center;aspect-ratio:1;">
<rect flex-css="position-type: absolute;width:100%;height:100%"
style="fill:#40e0d0;stroke-width:0.5" />
<image
xlink:href="{{info.thumbnail}}"
flex-css="height:60; width :60;"/>
</svg>
<svg class="left-column-bottom-layout" style="background-color:#fafbfc" flex-css="flex:1;margin-top:10;flex-direction: column;align-items:center">
<rect flex-css="position-type: absolute;width:100%;height:100%"
style="fill:#fafbfc;stroke:#000000;stroke-width:0.5;stroke-opacity:1" />
<text xml:space="preserve;" flex-css="margin-top:20" style="font-size:30">{{info.month}}</text>
<text xml:space="preserve;" flex-css="margin-top:10" style="font-size:30">{{info.day}}</text>
</svg>
</svg>
<svg class="right-column-layout" flex-css="padding:10;flex-direction: column;flex: 3;">
<svg flex-css="flex-direction: column;flex: 2 0 auto;">
<rect flex-css="position-type: absolute;width:100%;height:100%"
style="fill:#fafbfc;stroke:#000000;stroke-width:0.5;stroke-opacity:1" />
<svg flex-css="flex-direction: row;align-items: center;margin-top:20;padding:10;justify-content:space-between;">
<text xml:space="preserve" style="font-size:30">{{info.name}}</text>
<text xml:space="preserve">{{info.sport}}</text>
</svg>
<svg flex-css="flex-direction: row; align-items: center;padding:10;justify-content:space-between;">
<text>{{info.datetime}} | {{info.location}}</text>
<text>{{info.goingList.length}} Smiles</text>
</svg>
<svg flex-css="flex-direction: row;padding:10;">
<!--<text>{{info.location}}</text>-->
<svg flex-css="flex-direction: row;justify-content:flex-end;flex-wrap:wrap">
{{#info.goingList}}
<image
xlink:href="{{img}}"
flex-css="height:60; width :60;"/>
{{/info.goingList}}
</svg>
</svg>
</svg>
<svg flex-css="flex:5">
<svg flex-css="flex-direction: row;margin-top:10;justify-content:center;height:60; flex: none;">
<svg flex-css="flex-direction: column;justify-content:center;align-items:center;flex: 1;">
<rect flex-css="position-type: absolute;width:100%;height:100%"
style="fill:#fafbfc;stroke:#000000;stroke-width:0.5;stroke-opacity:1" />
<text>{{sideA.name}}</text>
</svg>
<svg flex-css="flex-direction: column;justify-content:center;align-items:center;flex: 1;">
<rect flex-css="position-type: absolute;width:100%;height:100%"
style="fill:#fafbfc;stroke:#000000;stroke-width:0.5;stroke-opacity:1" />
<text>{{sideB.name}}</text>
</svg>
</svg>
<svg flex-css="flex-direction: row;margin-top:10;justify-content:center;flex: 5;">
<svg flex-css="flex-direction: column;flex: 1;">
<rect flex-css="position-type: absolute;width:100%;height:100%"
style="fill:#fafbfc;stroke:#000000;stroke-width:0.5;stroke-opacity:1" />
</svg>
<svg flex-css="flex-direction: column;flex: 1;">
<rect flex-css="position-type: absolute;width:100%;height:100%"
style="fill:#fafbfc;stroke:#000000;stroke-width:0.5;stroke-opacity:1" />
</svg>
<image x="50%" y="50%" width="60" height="60"
xlink:href="./assets/icons/disappointed.png"/>
</svg>
<image x="10%" y="10%" width="60" height="60"
xlink:href="./assets/icons/confused.png"/>
<image x="60%" y="10%" width="60" height="60"
xlink:href="./assets/icons/smiling.png"/>
</svg>
</svg>
</svg>
<img src="isupport-layouts.png" width="600">
Layout Managers
Horizontal Layout
<svg flex-css="flex-direction: row;justify-content:space-between;align-items: center;padding:10;">
</svg>
Vertical Layout
<svg flex-css="flex-direction: column;align-items: center;margin:20;">
</svg>
Relative Layout
This is the standard behaviour for child elements in SVG. All the children of <SVG> are layed out relative to thier parent <SVG> tag.
<svg>
<image x="10%" y="10%" width="60" height="60"
xlink:href="./assets/icons/confused.png"/>
<image x="60%" y="10%" width="60" height="60"
xlink:href="./assets/icons/smiling.png"/>
</svg>
Flow Layout
{{#info.goingList}} {{/info.goingList}} are mustache binding variables that are replaced dynamically
<!--layout images right to left and wrap to next line if the content overflows-->
<svg flex-css="flex-direction: row;justify-content:flex-end;flex-wrap:wrap">
{{#info.goingList}}
<image
xlink:href="{{img}}"
flex-css="height:60; width :60;"/>
{{/info.goingList}}
</svg>
Side Affects
You could generate PNG images from SVG using npm modules like
Attribution
Inspired by Scott Logic Blog By Colin Eberhardt
Icons made by Roundicons from Flaticon is licensed by Creative Commons BY 3.0
Contribute
You are welcome to do a pull request. It would greatly help this module if it could find more
Related Skills
node-connect
348.2kDiagnose OpenClaw node connection and pairing failures for Android, iOS, and macOS companion apps
frontend-design
108.9kCreate distinctive, production-grade frontend interfaces with high design quality. Use this skill when the user asks to build web components, pages, or applications. Generates creative, polished code that avoids generic AI aesthetics.
openai-whisper-api
348.2kTranscribe audio via OpenAI Audio Transcriptions API (Whisper).
qqbot-media
348.2kQQBot 富媒体收发能力。使用 <qqmedia> 标签,系统根据文件扩展名自动识别类型(图片/语音/视频/文件)。
