Skip to content

Instantly share code, notes, and snippets.

@iconmaster
Last active October 19, 2017 20:41
Show Gist options
  • Save iconmaster/3473befd85a37cb0f8298f5d6b5b2a8f to your computer and use it in GitHub Desktop.
Save iconmaster/3473befd85a37cb0f8298f5d6b5b2a8f to your computer and use it in GitHub Desktop.
Recommendations for designing Framer modules for users

Capitalization

Framer employs lowerCamelCase for almost everything: functions, variables properties, etc. Classes, however, use TitleCase. Events are a bit of a special case, as explained under Events.

Constructors

Users are accustomed to instantiating objects with constructor syntax, so try to follow this approach in building your own classes.

myCustomClassInstance = new CustomClass
	# simple instance properties
	property: value0
	anotherProperty: value1
	# arrays in properties
	propertyArray: [
		value0,
		value1,
		value2, # trailing comma for consistency
	]
	actionArray: [
		-> action0(),
		-> action1(),
	]
	# objects in properties
	propertyObject:
		subObject0:
			key0: value0
		subObject1:
			key1: value1

Properties

Instance properties should be written as adjectives or nouns. myClass.hidden or myClass.titleFontWeight.

class MyClass extends Layer
	constructor: () ->
		@hidden = false
		@titleFontWeight = 400

Functions

Represent functions as meaningful verb phrases: updateLayout().

Prototype Functions

For purposes of memory optimization, try to design class functions as prototype functions so they are not recreated for each instance.

Prototype functions are included in the class definition but not in the constructor, and use a colon in place of an equals sign.

class MyLayer extends Layer
  constructor: () ->  
    @performPropertyFunction = () => 
      print "property" # try to avoid this type

  performPrototypeFunction: () -> 
    print "prototype"
Other Functions

Utility functions you intend to expose to the user outside of a class will tend to live on the exports object, but should follow the same capitalization and wording guidelines.

exports.performFunction = () ->

Named Parameters

If your function takes more than two parameters, or if the user is likely to confuse their order, consider using named parameters.

makeApple: ({appleColor, leafColor, size} = {}) ->
	appleColor ?= "red"
	leafColor ?= "green"
	size ?= 10
	
makeApple(appleColor: "green", leafColor: "yellow", size: 20)

Events

When an operation begins or ends, it can be helpful to emit an event so the user can chain other operations to it. Generally, only layers emit events. The emit statement is very simple:

myLayer.emit "myevent"

Event names are lowercase, but TitleCase when referenced as properties of the global Events variable (scroll.on Events.ScrollStart). Event shortcuts (scroll.onScrollStart) use lowerCamelCase.

If you want to support all these methods, you might extend Layer and Events like so:

myLayer.emit "myevent"
Events.MyEvent = "myevent"
Layer::onMyEvent = (cb) -> @on(Events.MyEvent, cb)

However, this is potentially risky -- you might overwrite an existing Event property. It is sufficient to provide the emitter, in lowercase.

Events are present tense verbs. Supply start and end event points as a pair or don't include start in the event name.

Layers

If your module generates layers, make sure each layer has a name to display in the Framer Studio sidebar. Arrange layers in a sensible hierarchy.

classInstance
└── row
	└── contentBlock
		└── label

For any layer you'd like the user to be able to reference directly, provide a means to do this via dot syntax.

Within the module this is done by storing layers as properties of their parents.

@.row = rowLayer
rowLayer.contentBlock = contentBlockLayer
contentBlockLayer.label = labelLayer

Readme

Readmes can vary, but we try to make sure every module's readme includes the following.

  • License, contributing and maintenance shields
  • Module name
  • Brief description
  • Installation (npm, manual, via Framer Modules app)
  • Adding it to your project (the require statement)
  • API / How to Use
    • Topics within API
  • Example project
  • Known issues, if applicable
  • Footer (Web, Github, Twitter, Medium)

Self-documentation

It's also good to include the following in the module's header comment:

  • Adding it to your project (the require statement)
  • API / How to Use
    • Topics within API
  • Known issues, if applicable
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment