These are Atlassian's internal guidelines, published for the reference of plugin developers. More thorough documentation can be found in the Plugin development guide. Not all of these guidelines are followed throughout Confluence yet, but they set the direction of our future work in the product's front-end.
It is imperative that functionality in Confluence separate content, presentation and behaviour for maintainable front end code. This means the following:
The remainder of this document describes how to achieve this in Confluence.
At the moment we have two simple naming rules:
Please note as of Confluence 3.5, the DOCTYPE will change to HTML5.
Markup must be valid HTML 4 Strict and follow the Atlassian HTML coding conventions.
Use meaningful tags in your markup and do not use markup for non-semantic formatting (eg. only use <strong> for text which should have strong emphasis) or layout (that means no <table>s for layout!).
For example, the following markup is suitable for the File menu in an application:
1 2<h3 class="menu-title">File</h3> <ul class="navigation menu"> <li id="menu-item-new-window"><a href="#">New Window</a> <span class="shortcut">⌘N</span></li> <li id="menu-item-new-tab"><a href="#">New Tab</a> <span class="shortcut">⌘T</span></li> ... </ul>
The use of meaningful tags, rather than a sea of tables and divs make it rendering the page without stylesheets possible, and provide better degradation in mobile browsers, Internet Explorer, and so on.
Assign classes and IDs that allow you to style the content appropriately. Try to place classes and IDs "higher up" the DOM to enable efficient styling. In most cases, there will be a container element that can be used for this purpose.
Bad:
1 2<div class="funky"> <p class="my-funky-style">Foo.</p> <p class="my-funky-style">Bar.</p> <p class="my-funky-style">Sin.</p> <p class="my-funky-style">Qua.</p> </div>
1 2.my-funky-style { ... }
Good:
1 2<div class="funky"> <p>Foo.</p> <p>Bar.</p> <p>Sin.</p> <p>Qua.</p> </div>
1 2.funky p { ... }
Use multiple classes in markup when required, but be aware that IE6 has limitations with parsing style selectors including multiple classes. Ensure IDs are unique within the page or Javascript code will not be able to access the elements properly.
Do not use inline script and style tags. Put them in separate CSS and JS files and use #requireResource
- see DOC:Including web resources below.
Put attribute values in double-quotes, use lower-case tags and attribute names in all cases. Do not use self-closing tags (e.g. <link />
) or include tags or attributes that are not part of the spec. Use closing tags for all elements that support them. We want our HTML to be valid and well laid out (don't guess, use the validator!).
Although HTML 4 allows the omission of attribute values for boolean-style attributes, do not omit attribute values in Confluence HTML. Rather, set the attribute value to the name of the attribute. For example: <input type="checkbox" name="subscribe" checked="checked">
. This allows better forwards-compatibility with XHTML/HTML5.
Be sure that you are familiar with how Confluence automatically HTML encodes template references.
There are two ways to have your web resources included in a page.
Within a velocity template, you can use #requireResource(
pluginKey:webResourceKey)
to tell the WebResourceManager
that you require a particular resource on this page. Note, this macro does not generate the actual markup but is done in conf-webapp/src/main/webapp/decorators/includes/header.vm
via $webResourceManager.getResources()
.
Example of velocity code to require some web resources
1 2#requireResource("confluence.web.resources:ajs") #requireResource("confluence.web.resources:page-editor")
If your resource depends on a resource already provided by Confluence (or another plugin), you can add this dependency in your web resource declaration in the plugin xml. Please see Web Resource Module for the full documentaiton.
Another way to get your web resources included on a page is via contexts in your web resource declaration in the plugin xml. You can also define your own context and get all the resources for your custom context included in the page. Please refer to Web Resource Module for the full documentation.
For more information about the declaration and inclusion of web resources, see: Including Javascript and CSS resources.
We no longer have the huge site-css.vm velocity template. This has been split up into separate css files:
The only dynamic styles in Confluence are the colors set by colour schemes, hence all color styling was extracted into colors-css.vm.
We also have a separate stylesheet for the setup wizard, setup.css
.
CSS resources are included in the following order:
#requireResource
such as master.css)Colour scheme and theme styles are also included in the header via the combined.css
action call. It essentially produces a set of imports to other css resources, hence the name 'combined'.
Sample output of combined.css
1 2@import "/s/1317/7/1/_/styles/colors.css?spaceKey=DOC"; @import "/s/1317/7/1.0/_/download/resources/com.atlassian.confluence.themes.default:styles/default-theme.css";
Note, the old monster main-action.css (i.e.StylesheetAction
) has now been deprecated and split into separate actions.
Use the shortest form wherever possible. That means using three character colours, combined margin declarations, simple selectors, and so on:
1 2.menu li.menu-item a { color: #fff; background: #8ad6e8; margin: 0; line-height: 1.2; padding: 5px 1em; }
Avoid child selectors like ul > li
. Instead use a class name of "first" for compatibility with IE. (You can use child selectors if you want to intentionally exclude IE, however.)
When designing a new section of Confluence, consider using a style reset to clear out default styles for lists, paragraps and so on.
Very often, it's desirable to serve custom styles to Internet Explorer. Confluence web resources can be marked as 'ieOnly' in order to be rendered in conditional comments only parsed by IE.
See Including Javascript and CSS resources for more details and an example of how to do this.
Stylesheet web resources can be include a 'media' parameter with a value of 'print' to have media='print' included on their <link>
tag so they are only used for printing. See the master-styles web resource in Confluence's web-resources.xml for an example. Any media type will be passed directly into the link tag, so you can also provide styles for handheld, projector media types, etc. as supported by your user's browsers.
See Including Javascript and CSS resources for more details and an example of how to do this.
Use closures to prevent unnecessary variables and functions being exposed in the global scope. For example, the code below binds an onclick handler to a button in the page without exposing itself or its variables in the global scope:
1 2jQuery(function ($) { // I am a closure, hear me roar! var i = 0; function increment() { alert(i++); } $("button.counter").click(increment); });
Don't introduce new global variables in JS. Rather put them under some namespace such as AJS.Editor.MyGlobalVariable
.
Don't mix Velocity and Javascript code. See DOC:Passing dynamic values to Javascript if you need to pass dynamic values to Javascript.
To avoid depending on a particular JavaScript library too much, we have atlassian.js ("AJS") as an abstraction on top of each particular library's functions. In Confluence 2.9, AJS wraps jQuery so many functions work in the same style as that library. Throughout Confluence's JavaScript, we should use AJS to make common function calls such as $
, toggleClassName
etc. wherever possible. This enables us to easily change the underlying JavaScript library later on if necessary.
In the past, we have used embedded event handling like (horribly) so:
Sample code from common-choosetheme.vm
1 2<tr bgcolor="ffffff" onMouseOver="style.backgroundColor='f0f0f0'" onMouseOut="style.backgroundColor='ffffff'" onclick="javascript:checkRadioButton('themeKey.default');">
We are now moving to binding event handlers in javascript, using the jquery's bind
function.
Sample code from page-editor.js
1 2AJS.toInit(function () { AJS.$("#markupTextarea").bind("click", function () { storeCaret(this); }); AJS.$("#markupTextarea").bind("select", function () { storeCaret(this); storeTextareaBits(); }); AJS.$("#markupTextarea").bind("keyup", function () { storeCaret(this); contentChangeHandler(); }); AJS.$("#markupTextarea").bind("change", function () { contentChangeHandler(); }); AJS.$("submit-buttons").bind("click", function (e) { AJS.Editor.contentFormSubmit(e); }); });
You may have noticed in the above example, all the binds are wrapped in a AJS.toInit()
function. This is only necessary if you require the code to be fired after DOMReady.
Unfortunately, we haven't been able to port all the embedded event handler code to javascript. If you encounter such code during development, please fix it up as you go
For advanced dynamic functionality, you can use jQuery directly. However, we don't allow jQuery to set the global $
variable (which is still used by Prototype.js), so you should use the 'jQuery' global variable as shown below.
To use jQuery properly in a JS file, do the following:
1 2jQuery(function ($) { // your code goes here // use '$' for jQuery calls });
To get translated strings in your javascript, use the following syntax:
1 2var label = AJS.I18n.getText("some.key"); var labelWithArgs = AJS.I18n.getText("some.other.key", "arg1", "arg2");
Then add the following transformation xml to you web resource definition in your atlassian-plugin.xml
.
1 2<web-resource key="whats-new-resources" name="What's New Web Resources"> <transformation extension="js"> <transformer key="jsI18n"/> </transformation> .... </web-resource>
We are now trying remove inline scripts that are scattered throughout Confluence. Most of these inline scripts are in the velocity templates so dynamic values such as i18n strings and values from actions can be used in the script. We now have a way around this via AJS.params. You simply need to define a fieldset
in your template with classes "hidden" and "parameters". AJS will automatically populate itself with the inputs defined in the fieldset.
Example from page-location-form.vm
1 2<fieldset class="hidden parameters"> <input type="hidden" id="editLabel" value="$action.getText('edit.name')"> <input type="hidden" id="doneLabel" value="$action.getText('done.name')"> <input type="hidden" id="showLocation" value="$action.locationShowing"> <input type="hidden" id="hasChildren" value="$!helper.action.page.hasChildren()"> <input type="hidden" id="availableSpacesSize" value="$action.availableSpaces.size()"> <input type="hidden" id="spaceKey" value="$action.space.key"> <input type="hidden" id="pageId" value="$pageId"> <input type="hidden" id="actionMode" value="$mode"> <input type="hidden" id="parentPageId" value="$!parentPage.id"> </fieldset>
With the above code, you would use the above i18n edit label by calling AJS.params.editLabel
in your javascript.
If your i18n message includes variables in the form {0}, {1}, etc. which are meant to be populated by JavaScript values, you can use the AJS.format()
function to present them. For example:
1 2$(".draftStatus").html( AJS.format(AJS.params.draftSavedMessage, time) // "Draft saved at {0}" );
The second and subsequent arguments to AJS.format
replace all instances of {0}, {1}, etc. in the first argument, which must be a String.
We have revised the way we do this in Confluence 4.0 with the use of meta
tags. A velocity macro #putMetadata
is available for your convenience to output the meta tags in the right format. Note that any duplicate calls to put metadata for the same key will be overridden.
1 2#putMetadata('page-id', $page.id)
This produces the following markup in the head
element of the page.
1 2<meta name="ajs-page-id" content="590712">
To access this data in javascript you can use the AJS.Meta.get
api:
1 2var pageId = AJS.Meta.get("page-id");
To view all the currently available meta tags on a page, see AJS.Meta.getAllAsMap()
.
In Confluence 4.0, you now have a convenient way to use a template in JavaScript using soy. More details are documented on this page.
Rate this page: