Thanks so much for wanting to help! We really appreciate it.
- Have an idea for a new feature?
- Want to add a new built-in theme?
Excellent! You've come to the right place.
- If you find a bug or wish to suggest a new feature, please create an issue first
- Make sure your code & comment conventions are in-line with the project's style (execute gometalinter as in .travis.yml file)
- Make your commits and PRs as tiny as possible - one feature or bugfix at a time
- Write detailed commit messages, in-line with the project's commit naming conventions
This file contains instructions on adding themes to Hermes:
We use Golang templates under the hood to inject the e-mail body into themes.
If you want to supply your own custom theme for Hermes to use (but don't want it included with Hermes):
- Create a new struct implementing
Theme
interface (hermes.go). A real-life example is in default.go - Supply your new theme at hermes creation
type MyCustomTheme struct{}
func (dt *MyCustomTheme) Name() string {
return "mycustomthem"
}
func (dt *MyCustomTheme) HTMLTemplate() string {
// Get the template from a file (if you want to be able to change the template live without retstarting your application)
// Or write the template by returning pure string here (if you want embbeded template and do not bother with external dependencies)
return "<A go html template with wanted information>"
}
func (dt *MyCustomTheme) PlainTextTemplate() string {
// Get the template from a file (if you want to be able to change the template live without retstarting your application)
// Or write the template by returning pure string here (if you want embbeded template and do not bother with external dependencies)
return "<A go plaintext template with wanter information>"
}
h := hermes.Hermes{
Theme: new(MyCustomTheme) // Set your fresh new theme here
Product: hermes.Product{
Name: "Hermes",
Link: "https://example-hermes.com/",
},
}
// ...
// Continue with the rest as usual, create your email and generate the content.
// ...
- That's it.
If you want to create a new built-in Hermes theme:
- Fork the repository to your GitHub account and clone it to your computer
- Create a new Go file named after your new theme
- Copy content of default.go file in new file and make any necessary changes
- Scroll down to the injection snippets and copy and paste each code snippet into the relevant area of your template markup
- Test the theme by adding the theme to slice of tested themes (see hermes_test.go). A set of tests will be run to check that your theme follows features of Hermes.
- Create examples in new folder for your theme in
examples
folder and rungo run *.go
. It will generate the differenthtml
andplaintext
emails for your different examples. Follow the same examples as default theme (3 examples: Welcome, Reset and Receipt) - Add the theme name, credit, and screenshots to the
README.md
file's Supported Themes section (copy one of the existing themes' markup and modify it accordingly) - Submit a pull request with your changes and we'll let you know if anything's missing!
Thanks again for your contribution!
The following will inject either the product logo or name into the template.
<a href="{{.Hermes.Product.Link}}" target="_blank">
{{ if .Hermes.Product.Logo }}
<img src="{{.Hermes.Product.Logo}}" class="email-logo" />
{{ else }}
{{ .Hermes.Product.Name }}
{{ end }}
</a>
It's a good idea to add the following CSS declaration to set max-height: 50px
for the logo:
.email-logo {
max-height: 50px;
}
The following will inject the e-mail title (Hi John Appleseed,) or a custom title provided by the user:
<h1>{{if .Email.Body.Title }}{{ .Email.Body.Title }}{{ else }}{{ .Email.Body.Greeting }} {{ .Email.Body.Name }},{{ end }}</h1>
The following will inject the intro text (string or array) into the e-mail:
{{ with .Email.Body.Intros }}
{{ if gt (len .) 0 }}
{{ range $line := . }}
<p>{{ $line }}</p>
{{ end }}
{{ end }}
{{ end }}
The following will inject a <dl>
of key-value pairs into the e-mail:
{{ with .Email.Body.Dictionary }}
{{ if gt (len .) 0 }}
<dl class="body-dictionary">
{{ range $entry := . }}
<dt>{{ $entry.Key }}:</dt>
<dd>{{ $entry.Value }}</dd>
{{ end }}
</dl>
{{ end }}
{{ end }}
It's a good idea to add this to the top of the template to improve the styling of the dictionary:
/* Dictionary */
.dictionary {
width: 100%;
overflow: hidden;
margin: 0 auto;
padding: 0;
}
.dictionary dt {
clear: both;
color: #000;
font-weight: bold;
margin-right: 4px;
}
.dictionary dd {
margin: 0 0 10px 0;
}
The following will inject the table into the e-mail:
<!-- Table -->
{{ with .Email.Body.Table }}
{{ $data := .Data }}
{{ $columns := .Columns }}
{{ if gt (len $data) 0 }}
<table class="data-wrapper" width="100%" cellpadding="0" cellspacing="0">
<tr>
<td colspan="2">
<table class="data-table" width="100%" cellpadding="0" cellspacing="0">
<tr>
{{ $col := index $data 0 }}
{{ range $entry := $col }}
<th
{{ with $columns }}
{{ $width := index .CustomWidth $entry.Key }}
{{ with $width }}
width="{{ . }}"
{{ end }}
{{ $align := index .CustomAlignment $entry.Key }}
{{ with $align }}
style="text-align:{{ . }}"
{{ end }}
{{ end }}
>
<p>{{ $entry.Key }}</p>
</th>
{{ end }}
</tr>
{{ range $row := $data }}
<tr>
{{ range $cell := $row }}
<td
{{ with $columns }}
{{ $align := index .CustomAlignment $cell.Key }}
{{ with $align }}
style="text-align:{{ . }}"
{{ end }}
{{ end }}
>
{{ $cell.Value }}
</td>
{{ end }}
</tr>
{{ end }}
</table>
</td>
</tr>
</table>
{{ end }}
{{ end }}
It's a good idea to add this to the top of the template to improve the styling of the table:
/* Table */
.data-wrapper {
width: 100%;
margin: 0;
padding: 35px 0;
}
.data-table {
width: 100%;
margin: 0;
}
.data-table th {
text-align: left;
padding: 0px 5px;
padding-bottom: 8px;
border-bottom: 1px solid #DEDEDE;
}
.data-table th p {
margin: 0;
font-size: 12px;
}
.data-table td {
text-align: left;
padding: 10px 5px;
font-size: 15px;
line-height: 18px;
}
The following will inject the action link (or button) into the e-mail:
{{ with .Email.Body.Actions }}
{{ if gt (len .) 0 }}
{{ range $action := . }}
<p>{{ $action.Instructions }}</p>
<table class="body-action" align="center" width="100%" cellpadding="0" cellspacing="0">
<tr>
<td align="center">
<div>
<a href="{{ $action.Button.Link }}" class="button" style="background-color: {{ $action.Button.Color }}" target="_blank">{{ $action.Button.Text }}</a>
</div>
</td>
</tr>
</table>
{{ end }}
{{ end }}
{{ end }}
A good practice is to describe action in footer in case of problem when displaying button and CSS. The text for the description is provided through the TroubleText
field of the Product
struct. The text may contain a placeholder {ACTION}
which is expected to be replaced with the text of the button. The default value of TroubleText
is If you’re having trouble with the button '{ACTION}', copy and paste the URL below into your web browser.
{{ with .Email.Body.Actions }}
<table class="body-sub">
<tbody><tr>
{{ range $action := . }}
<td>
<p class="sub">{{$.Hermes.Product.TroubleText | replace "{ACTION}" $action.Button.Text}}</p>
<p class="sub"><a href="{{ $action.Button.Link }}">{{ $action.Button.Link }}</a></p>
</td>
{{ end }}
</tr>
</tbody>
</table>
{{ end }}
Be aware that Outlook HTML engine is very old and is not compatible with many CSS features. It means, if you want to create a button, the best solution is to create a case only for Outlook. For example, in flat theme, the following code is used to create a button which is a Microsoft VML rectangle. It will not be as perfect as pure CSS interpreted by recent engines, but it will do the work.
{{safe "<!--[if mso]>" }}
<div style="margin: 30px auto">
<v:roundrect xmlns:v="urn:schemas-microsoft-com:vml"
xmlns:w="urn:schemas-microsoft-com:office:word"
href="{{ $action.Button.Link }}"
style="height:45px;v-text-anchor:middle;width:570px;background-color:{{ if $action.Button.Color }}{{ $action.Button.Color }}{{else}}#00948D{{ end }};"
arcsize="0%"
{{ if $action.Button.Color }}strokecolor="{{ $action.Button.Color }}" fillcolor="{{ $action.Button.Color }}"{{ else }}strokecolor="#00948D" fillcolor="#00948D"{{ end }}
>
<w:anchorlock/>
<center style="color: {{ if $action.Button.TextColor }}{{ $action.Button.TextColor }}{{else}}#FFFFFF{{ end }};font-size: 15px;text-align: center;font-family:sans-serif;font-weight:bold;">
{{ $action.Button.Text }}
</center>
</v:roundrect>
</div>
{{safe "<![endif]-->" }}
When the action is an invite code, use this kind of code:
{{ if $action.InviteCode }}
<div style="margin-top:30px;margin-bottom:30px">
<table class="body-action" align="center" width="100%" cellpadding="0" cellspacing="0">
<tr>
<td align="center">
<table align="center" cellpadding="0" cellspacing="0" style="padding:0;text-align:center">
<tr>
<td style="display:inline-block;border-radius:3px;font-family:Consolas, monaco, monospace;font-size:28px;text-align:center;letter-spacing:8px;color:#555;background-color:#eee;padding:20px">
{{ $action.InviteCode }}
</td>
</tr>
</table>
</td>
</tr>
</table>
</div>
{{ end }}
The following will inject the outro text (string or array) into the e-mail:
{{ with .Email.Body.Outros }}
{{ if gt (len .) 0 }}
{{ range $line := . }}
<p>{{ $line }}</p>
{{ end }}
{{ end }}
{{ end }}
The following will inject the signature phrase (e.g. Yours truly) along with the product name into the e-mail:
{{.Email.Body.Signature}},
<br>
{{.Hermes.Product.Name}}
The following will inject the copyright notice into the e-mail:
{{.Hermes.Product.Copyright}}
In order to support generating RTL e-mails, inject the textDirection
variable into the <body>
tag:
<body dir="{{.Hermes.TextDirection}}">
In order to support Markdown free content, inject the following code:
{{ if (ne .Email.Body.FreeMarkdown "") }}
{{ .Email.Body.FreeMarkdown.ToHTML }}
{{ else }}
[... Here is the templating for dictionary, table and actions]
{{ end }}
```