CSS Best Practices

CSS usage in portlets requires care by the portlet developers to avoid styling conflicts with both the portal framework and other portlets on the same page.Below are uPortal's recommendations for using CSS in your portlets to help mitigate these issues.

CSS Loading

Unfortunately the JSR-168 specification doesn't provide a way for a portlet to contribute to the <head> of a document for things like loading CSS files. That leaves the best option for a developer to load the CSS frameworks that the portlet needs inline in the HTML markup of the page. Below is an example of loading a CSS file in the portlet content.

<!-- Store the portlet namespace string in the JSP page variable "n" -->
<c:set var="n"><portlet:namespace/></c:set>

<link rel="stylesheet" href="<c:url value='/css/my_portlet.css'/>" type="text/css"/>
<div class="MyPortletWebapp" id="${n}container">
    <!-- portlet content here -->
<div>

The benefit of a namespaced id in the div containing the portlet content is discussed on the page JavaScript Best Practices.

Update:  uPortal 4.1.0 Respondr supports the RENDER_HEADERS subphase so a portlet that chooses to can contribute markup for inclusion into the head section (see

Error rendering macro 'jira' : Unable to locate Jira server for this macro. It may be due to Application Link configuration.
).  However unless you are working on a framework portlet (e.g. a portlet that is part of uportal-war) you can't guarantee that the portal version your portlet is running on supports that feature so you should follow the above practice.

Content Distribution Networks (CDNs)

Hold off on using CDN URLs until Unable to locate Jira server for this macro. It may be due to Application Link configuration. is implemented. Otherwise it becomes a problem when you are attempting to work on uPortal or do demos in offline mode.

 

Some popular CSS files are hosted on CDNs.  To decrease load times (browser accesses a different domain) and potentially improve performance (a different application may have downloaded the CSS file from the CDN and have it in the browser's cache), a CDN can be used instead of obtaining the file from the Resource Server.  The CDN must support HTTPS to access the library so user's don't get security warnings.

When a portlet obtains a CSS file from a CDN instead of the Resource Server, the library does not need to be mentioned in the maven-war-plugin in the portlet's pom.xml (don't need to have a local copy of the file overlaid into the portlet).

CDNs do have some disadvantages.  

  • They might make it more difficult to run uPortal off-line when you don't have a network connection and the browser doesn't have a cached version of the desired file.  You can work around this by temporarily modifying the source code, or creating an entry in your machine's hosts file and  hosting the CSS file locally (see below).
  • CSS files hosted on the Resource Server in both non-minified and minified versions allow selecting the non-minified versions by disabling Aggregation in the uPortal Admin UI.  If CSS files are listed in a skin.xml file and included with the <rs:aggregatedResources path="skin.xml"/> tag you do get minified or non-minified versions accordingly.

CDNs are recommended for the following CSS files.  CSS fies referenced on CDNs should also be placed in the resource server so they are available when off-line in an emergency (see above).

  • Font Awesome

Preferred way to access resources from a CDN is to put them in a skin.xml file and use the rs:aggregatedResources tag so you get the non-minified versions when desired.  

Preferred way to access CDN content
skin.xml:
 
<resources xmlns="http://www.jasig.org/uportal/web/skin"  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
 xsi:schemaLocation="http://www.jasig.org/uportal/web/skin https://source.jasig.org/schemas/resource-server/skin-configuration/skin-configuration-v1.2.xsd">
 
  <css included="plain">//maxcdn.bootstrapcdn.com/font-awesome/4.1.0/css/font-awesome.css</css>
  <css included="aggregated">//maxcdn.bootstrapcdn.com/font-awesome/4.1.0/css/font-awesome.min.css</css>

</resources>
 
JSP file:
 
<rs:aggregatedResources path="skin.xml"/>

The alternative is to load them from the CDNs directly as you would in any webapp.

non-preferred way to access CDN content
<link rel="stylesheet" href="//maxcdn.bootstrapcdn.com/font-awesome/4.1.0/css/font-awesome.min.css" type="text/css"/>

CSS Scoping

You'll notice in the first example above in CSS Loading that immediately after the CSS <link> element is a div with a class name specific to the portlet web application. This div is used to scope all style rules created for the portlet.

Scoping your portlet's CSS under an outer div named specifically for your portlet web application has two important advantages:

  1. Your scoped CSS rules will be the most specific styles for your elements and take precedence over other styles on the page.
  2. Your scoped CSS rules will not apply to any elements outside of your wrapper div preventing unintended side effects on other content on the page.

In the example below the text-decoration style is changed for all <a> elements but only under the wrapper div for this portlet.

my_portlet.css
.MyPortletWebapp a {
    text-decoration: none;
}
.MyPortletWebapp a:hover {
    text-decoration: underline;
}

Using Bootstrap CSS in your portlets

Bootstrap is used in uPortal 4.1 Respondr theme.  You can use Bootstrap CSS in your portlets (and Bootstrap JS - see JavaScript Best Practices), but you need to add the css class 'bootstrap-styles' to the base div or your portlet and include a special version of Bootstrap CSS in the Resource Server webapp that respects the namespace.  This allows your portlet to use bootstrap CSS without impacting the other portlets or page that may not use Boostrap, as in the case of using the Universality theme.

<link rel="stylesheet" href="<rs:resourceURL value='/rs/bootstrap-namespaced/3.1.1/css/bootstrap.min.css'/>" type="text/css" />


...
<!-- code to include jQuery and Bootstrap JS as documented in Javascript Best Practices page -->
...
 
<div class="MyPortletWebapp bootstrap-styles" id="${n}">
...
</div>

IDs versus Classes

Many CSS developers coming from standard web application development are used to targeting CSS rules to elements based on the element's ID. With portlets your content could be included multiple times on the page or other portlets on the page could have conflicting IDs. The solution for the conflicting ID problem with portlets is to use a unique namespace string provided by the portlet API. The problem this then presents for CSS is that the IDs for the elements are not static. The recommendation then is to only ever target CSS rules for portlets by class name and never by ID.

Development Tips

CSS aggregation tip

On your local and dev instance, you can permanently disable JavaScript and CSS aggregation by adding to your .bash_profile:

export JAVA_OPTS="$JAVA_OPTS -Dorg.jasig.resourceserver.utils.aggr.aggregated_theme=false”
Also see UE Development Tips