blogger_theme 5.0.0
blogger_theme: ^5.0.0 copied to clipboard
A lightweight standalone Dart library to design Blogger themes with Jaspr-like syntax.
🚀 blogger_theme #
What is blogger_theme? #
blogger_theme is a lightweight Dart library for generating Blogger (Blogspot) theme XML using a clean, declarative component API inspired by Jaspr.
It removes the need to write raw Blogger template XML by hand and provides reusable Dart components for common Blogger template elements such as b:section, b:widget, b:if, b:loop, and b:skin.
Features #
- Declarative theme authoring in Dart using
Component,DomComponent, andText. - Pure Dart implementation with zero runtime dependencies.
- Safe XML rendering for Blogger templates, including escaping special characters and filtering XML 1.0 control characters.
- Blogger-native helpers for sections, widgets, conditionals, loops, includes, and template fragments.
- Client script support with
BClientScriptfor Dart-to-JS inline script injection. - Flexible theme export using
BloggerTheme.generate().
Installation #
Add blogger_theme to your pubspec.yaml:
dependencies:
blogger_theme: ^5.0.0
Or add it directly with:
dart pub add blogger_theme
Run:
dart pub get
References #
- Documentation: https://bloggerquickstart.blogspot.com/
- Video tutorials: https://youtube.com/antinna
- Sponsor the project: https://github.com/sponsors/manishmg3994
Quick Start #
1. Define your layout component #
import 'package:blogger_theme/blogger_theme.dart';
class BlogLayout extends Component {
const BlogLayout();
@override
Iterable<Component> build() => [
Div(
attributes: {'class': 'wrapper-pane'},
children: [
BSection(
id: 'header-area',
className: 'header-section',
maxwidgets: 1,
showaddelement: true,
children: [],
),
BSection(
id: 'main-content',
className: 'main-content-section',
maxwidgets: 1,
showaddelement: true,
children: [
H1(
children: [
Text('My AMP Blogger Theme having H1 Keycolor in '),
//<b:eval expr="data:skin.vars.keycolor"/></h1>
BEval(expr: 'data:skin.vars.keycolor'),
],
),
],
),
],
),
];
}
2. Generate Blogger theme XML (with AMP-compliant overrides) #
import 'package:blogger_theme/blogger_theme.dart';
void main() {
final theme = BloggerTheme(
attributes: {
// static compiler flags
'b:responsive': 'true',
'b:defaultwidgetversion': '2',
'b:layoutsversion': '3',
'b:css': 'false', // Disables default Blogger CSS globally
/// this removes that <script src=".../widgets.js">
/// for Better do try replace the closing </body> with
/// => <!--</body>--></body>
/// => this will make those scripts as comment
/// Reference: [JS as Comment] Below
// 'b:js': 'false', // Disables default widgets.js globally
//
'xmlns': 'http://www.w3.org/1999/xhtml',
'xmlns:b': 'http://www.google.com/2005/gml/b',
'xmlns:data': 'http://www.google.com/2005/gml/data',
'xmlns:expr': 'http://www.google.com/2005/gml/expr',
},
// Reset attributes to clean XML output for strict AMP validation
children: [
BAttr(name: 'xmlns', value: ''),
BAttr(name: 'xmlns:b', value: ''),
BAttr(name: 'xmlns:expr', value: ''),
BAttr(name: 'xmlns:data', value: ''),
// Conditionally adds amp="amp" (or lightning bolt symbol ⚡) to <html> on mobile requests
BAttr(cond: 'data:blog.isMobileRequest', name: 'amp', value: 'amp'),
// Alternatively, you can use the ⚡ symbol:
// BAttr(cond: 'data:blog.isMobileRequest', name: '⚡', value: '⚡'),
// BClass(exprName: '"nJs" + (data:blog.isMobileRequest ? " amp" : "")'), // TODO:[Kept to show we can assign class ]
],
head: [
Meta(attributes: {"expr:charset": "data:blog.encoding"}),
Script(
attributes: {
'async': 'async',
'src': 'https://cdn.ampproject.org/v0.js',
},
),
Meta(
attributes: {
"name": "viewport",
"content": "width=device-width,minimum-scale=1,initial-scale=1",
},
),
Link(
attributes: {"rel": "canonical", "expr:href": "data:blog.canonicalUrl"},
),
Link(
attributes: {
"rel": "amphtml",
"expr:href": 'data:blog.canonicalUrl params { m: "1" }',
},
),
Title(children: [Text('Generated Blogger Theme')]),
// RawText(
// '<style amp-boilerplate="amp-boilerplate">body{-webkit-animation:-amp-start 8s steps(1,end) 0s 1 normal both;-moz-animation:-amp-start 8s steps(1,end) 0s 1 normal both;-ms-animation:-amp-start 8s steps(1,end) 0s 1 normal both;animation:-amp-start 8s steps(1,end) 0s 1 normal both}@-webkit-keyframes -amp-start{from{visibility:hidden}to{visibility:visible}}@-moz-keyframes -amp-start{from{visibility:hidden}to{visibility:visible}}@-ms-keyframes -amp-start{from{visibility:hidden}to{visibility:visible}}@-o-keyframes -amp-start{from{visibility:hidden}to{visibility:visible}}@keyframes -amp-start{from{visibility:hidden}to{visibility:visible}}</style>'
// '<noscript><style amp-boilerplate="amp-boilerplate">body{-webkit-animation:none;-moz-animation:none;-ms-animation:none;animation:none}</style></noscript>',
// ),
// OR For Better Tooling Support USE [In Future]
Style(
attributes: {"amp-boilerplate": "amp-boilerplate"},
children: [
Text(
'body{-webkit-animation:-amp-start 8s steps(1,end) 0s 1 normal both;-moz-animation:-amp-start 8s steps(1,end) 0s 1 normal both;-ms-animation:-amp-start 8s steps(1,end) 0s 1 normal both;animation:-amp-start 8s steps(1,end) 0s 1 normal both}@-webkit-keyframes -amp-start{from{visibility:hidden}to{visibility:visible}}@-moz-keyframes -amp-start{from{visibility:hidden}to{visibility:visible}}@-ms-keyframes -amp-start{from{visibility:hidden}to{visibility:visible}}@-o-keyframes -amp-start{from{visibility:hidden}to{visibility:visible}}@keyframes -amp-start{from{visibility:hidden}to{visibility:visible}}',
escape: false,
),
],
),
Noscript(
children: [
Style(
attributes: {"amp-boilerplate": "amp-boilerplate"},
children: [
Text(
'body{-webkit-animation:none;-moz-animation:none;-ms-animation:none;animation:none}',
escape: false,
),
],
),
],
),
// For AMP compliance, keep the CSS string empty ("") and use Style with `amp-custom` attribute and there put your css (limit 50KB)
BIf(
cond: false.toString(),
children: [
BSkin(
"",
variables: [
// Text('''
// /*
// <!-- Variable definitions -->
// <Variable name="keycolor" description="Main Color" type="color" default="#8abc0d" value="#ee582e"/>
// */
// '''),
BVariable(
name: "keycolor", // Variable identifier used in CSS
description: "Main Color", // Title shown in Blogger Designer
type: "color", // Tells Blogger to render a color picker
defaultValue: "#8abc0d", // Default color
value: "#ee582e",
),
// TODO: Add variables and Group here
// Disable default b:skin CSS output while preserving custom Blogger theme variables.
// Keep the CSS string empty ("") for strict AMP validation.
],
),
],
),
// Place Use custom styles in a <style amp-custom> tag instead (50 KB limit).
Style(
attributes: {
'amp-custom': 'amp-custom',
'id': "page-skin-1",
"type": "text/css",
},
children: [
// as we made skin:vars as vars there in top with
// Your Custom css styles here, for example:
Text(
'''
:root {
--keycolor: <data:skin.vars.keycolor/>;
}
body {
font-family: Arial, sans-serif;
margin: 0;
padding: 0;
}
h1 {
color: var(--keycolor);
}
''',
escape: false, // This is Important as if escaped
//then <data:skin.vars.keycolor/> will be converted and that will not work as aspected
),
],
),
// here we have a Issue with amp if
// your Earnings Tab > is Enabled from Blogger Dashboard,
// then you need to remove the Adsense from that Earning Tab
// and trick it to use with other methods. and also make sure you
// don't click on that `Connect Adsense` button from Blogger Dashboard,z
],
body: [const BlogLayout()],
);
var xml = theme.generate();
// Reference: [JS as Comment]
xml = xml.replaceLast(
'</body>',
// or use => <!--</body>--></body>
'''<textarea id='template_widgets_js' disabled='disabled' readonly='readonly' hidden='hidden' aria-hidden='true' class='notranslate'>
</body>
</textarea>
</body>''',
);
print(xml);
}
extension StringUtils on String {
/// Replaces the last occurrence of [from] with [to].
String replaceLast(Pattern from, String to) {
final match = from.allMatches(this).lastOrNull;
if (match == null) return this;
return replaceRange(match.start, match.end, to);
}
}
API Overview #
Core building blocks #
ComponentDomComponentTextRawTextFragmentRenderer
Blogger-specific components #
BSection,BWidget,BWidgetSettings,BWidgetSettingBIf,BElseIf,BElseBLoop,BData,BArg,BAttr,BClassBInclude,BIncludable,BTag,BEvalBSkinBClientScript
HTML helper components #
- Standard HTML wrappers like
Divplus other helpers inhtml_components.dart
AMP (Accelerated Mobile Pages) components #
- Full-featured AMP HTML support under
lib/src/amp/category files. - 80+ AMP elements covered, including basic media (
AmpImg,AmpVideo,AmpAudio), layouts (AmpCarousel,AmpBaseCarousel,AmpSidebar,AmpAccordion,AmpLightbox), social embeds (AmpYoutube,AmpInstagram), web stories (AmpStory,AmpStoryPage,AmpStoryGridLayer), dynamic bindings (AmpState,AmpList,AmpMustache), and paywalls (AmpAccess). - Core document layouts:
AmpHtml,AmpCharset,AmpViewport,AmpCanonical. - Mandatory boilerplate loader elements:
AmpBoilerplate,AmpRuntimeScript,AmpExtensionScript. - Pre-compilation Static Analysis: Integrated
AmpValidatorutility to audit and validate your rendered theme pages against standard AMP specifications.
Project Structure #
lib/blogger_theme.dart— public exportslib/src/core.dart— component model and rendererlib/src/blogger_components.dart— Blogger template helperslib/src/html_components.dart— HTML helper componentslib/src/client_script.dart— Dart-to-JS script supportlib/src/theme_utility.dart— theme generation utilitiesexample/main.dart— sample theme generation entrypoint
Contributing #
Contributions are welcome!
- Found a bug? Open an issue: https://github.com/antinna/blogger_theme/issues
- Want to improve the library? Fork the repo and submit a pull request.
License #
blogger_theme is licensed under the MIT License. See LICENSE for details.